gpt4 book ai didi

C#序列化数据

转载 作者:行者123 更新时间:2023-11-30 15:12:35 24 4
gpt4 key购买 nike

我一直在使用 BinaryFormatter 将数据序列化到磁盘,但它的可扩展性似乎不高。我已经创建了一个 200Mb 的数据文件,但无法将其读回(在解析完成之前遇到流结束)。它尝试反序列化大约 30 分钟,然后放弃。这是在具有 8Gb RAM 的相当不错的四核机器上。

我正在序列化一个相当大的复杂结构。

htCacheItems 是缓存项的哈希表。每个 CacheItem 都有几个简单的成员(字符串 + 整数等),还包含一个 Hashtable 和一个链表的自定义实现。子哈希表指向 CacheItemValue 结构,该结构目前是一个包含键和值的简单 DTO。链表项也同样简单。

失败的数据文件包含大约 400,000 个 CacheItemValues。

较小的数据集运行良好(尽管反序列化和使用大量内存的时间比我预期的要长)。

    public virtual bool Save(String sBinaryFile)
{
bool bSuccess = false;
FileStream fs = new FileStream(sBinaryFile, FileMode.Create);

try
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, htCacheItems);
bSuccess = true;
}
catch (Exception e)
{
bSuccess = false;
}
finally
{
fs.Close();
}
return bSuccess;
}

public virtual bool Load(String sBinaryFile)
{
bool bSuccess = false;

FileStream fs = null;
GZipStream gzfs = null;

try
{
fs = new FileStream(sBinaryFile, FileMode.OpenOrCreate);

if (sBinaryFile.EndsWith("gz"))
{
gzfs = new GZipStream(fs, CompressionMode.Decompress);
}

//add the event handler
ResolveEventHandler resolveEventHandler = new ResolveEventHandler(AssemblyResolveEventHandler);
AppDomain.CurrentDomain.AssemblyResolve += resolveEventHandler;

BinaryFormatter formatter = new BinaryFormatter();
htCacheItems = (Hashtable)formatter.Deserialize(gzfs != null ? (Stream)gzfs : (Stream)fs);

//remove the event handler
AppDomain.CurrentDomain.AssemblyResolve -= resolveEventHandler;

bSuccess = true;
}
catch (Exception e)
{
Logger.Write(new ExceptionLogEntry("Failed to populate cache from file " + sBinaryFile + ". Message is " + e.Message));
bSuccess = false;
}
finally
{
if (fs != null)
{
fs.Close();
}
if (gzfs != null)
{
gzfs.Close();
}
}
return bSuccess;
}

resolveEventHandler 只是一种解决方法,因为我在一个应用程序中序列化数据并将其加载到另一个应用程序 (http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/e5f0c371-b900-41d8-9a5b-1052739f2521)

问题是,我该如何改进呢?数据序列化总是效率低下吗,我最好编写自己的例程吗?

最佳答案

我个人会尽量避免使用 assembly-resolve;它有一定的气味。如果您必须使用BinaryFormatter,那么我只需将 DTO 放入可在两个应用程序中使用的单独库 (dll) 中。

如果您不想共享 dll,那么在我看来,您不应该使用 BinaryFormatter - 您应该使用基于契约的序列化程序,例如 XmlSerializerDataContractSerializer,或“ Protocol Buffer ”实现之一(并重复 Jon 的免责声明:我写了 one of the others )。

200MB 看起来确实很大,但我没想到它会失败。一个可能的原因是它为引用所做的对象跟踪;但即便如此,这还是让我感到惊讶。

我很想看到一个简化的对象模型,看看它是否“适合”上述任何一项。


这是一个尝试使用 protobuf-net 从描述中镜像您的设置的示例。奇怪的是,使用链表 which I'll investigate 时似乎出现了故障;但其余的似乎有效:

using System;
using System.Collections.Generic;
using System.IO;
using ProtoBuf;
[ProtoContract]
class CacheItem
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public int AnotherNumber { get; set; }
private readonly Dictionary<string, CacheItemValue> data
= new Dictionary<string,CacheItemValue>();
[ProtoMember(3)]
public Dictionary<string, CacheItemValue> Data { get { return data; } }

//[ProtoMember(4)] // commented out while I investigate...
public ListNode Nodes { get; set; }
}
[ProtoContract]
class ListNode // I'd probably expose this as a simple list, though
{
[ProtoMember(1)]
public double Head { get; set; }
[ProtoMember(2)]
public ListNode Tail { get; set; }
}
[ProtoContract]
class CacheItemValue
{
[ProtoMember(1)]
public string Key { get; set; }
[ProtoMember(2)]
public float Value { get; set; }
}
static class Program
{
static void Main()
{
// invent 400k CacheItemValue records
Dictionary<string, CacheItem> htCacheItems = new Dictionary<string, CacheItem>();
Random rand = new Random(123456);
for (int i = 0; i < 400; i++)
{
string key;
CacheItem ci = new CacheItem {
Id = rand.Next(10000),
AnotherNumber = rand.Next(10000)
};
while (htCacheItems.ContainsKey(key = rand.NextString())) {}
htCacheItems.Add(key, ci);
for (int j = 0; j < 1000; j++)
{
while (ci.Data.ContainsKey(key = rand.NextString())) { }
ci.Data.Add(key,
new CacheItemValue {
Key = key,
Value = (float)rand.NextDouble()
});
int tail = rand.Next(1, 50);
ListNode node = null;
while (tail-- > 0)
{
node = new ListNode
{
Tail = node,
Head = rand.NextDouble()
};
}
ci.Nodes = node;
}
}
Console.WriteLine(GetChecksum(htCacheItems));
using (Stream outfile = File.Create("raw.bin"))
{
Serializer.Serialize(outfile, htCacheItems);
}
htCacheItems = null;
using (Stream inFile = File.OpenRead("raw.bin"))
{
htCacheItems = Serializer.Deserialize<Dictionary<string, CacheItem>>(inFile);
}
Console.WriteLine(GetChecksum(htCacheItems));
}
static int GetChecksum(Dictionary<string, CacheItem> data)
{
int chk = data.Count;
foreach (var item in data)
{
chk += item.Key.GetHashCode()
+ item.Value.AnotherNumber + item.Value.Id;
foreach (var subItem in item.Value.Data.Values)
{
chk += subItem.Key.GetHashCode()
+ subItem.Value.GetHashCode();
}
}
return chk;
}
static string NextString(this Random random)
{
const string alphabet = "abcdefghijklmnopqrstuvwxyz0123456789 ";
int len = random.Next(4, 10);
char[] buffer = new char[len];
for (int i = 0; i < len; i++)
{
buffer[i] = alphabet[random.Next(0, alphabet.Length)];
}
return new string(buffer);
}
}

关于C#序列化数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1142069/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com