当使用c# BinaryFormatter.Deserialize时,是什么导致UI出现问题?
本文关键字:UI 问题 是什么 BinaryFormatter Deserialize | 更新日期: 2023-09-27 17:50:42
我使用BinaryFormatter来序列化和反序列化条目列表到byte[]数组中,并且我注意到对于列表中的大量元素,我的UI将挂起或有"打嗝"。当我说重要时,我指的是10K个项目(每个项目都有自己的项目集合)。
有趣的是,序列化和反序列化发生在一个单独的线程上,所以我不会认为会发生UI中断。我只是好奇是否有人处理过类似的事情,以及是否有任何解决方法。
下面的代码片段来自我正在测试的剥离的sol'n。当我在这里使用BinaryFormatter读取和写入磁盘时,我更感兴趣的部分是SerializationHelper类中的内容(它在内存中完成)。
我也意识到UI线程将被中断以发布状态更新,但这些可以忽略不计。我注意到当BinaryFormatter时UI挂起了。反序列化正在执行,UI上没有任何更新。
DataReader :
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializationTesting
{
internal class DataReader : DataIOBase
{
#region Constants
private const int MAX_STEPS = 2;
#endregion
#region Declarations
private string _file;
#endregion
public DataReader(string file)
{
_file = file;
}
protected override void DoWork()
{
UpdateStatus(0, MAX_STEPS, "Reading from file...");
byte[] serializedData = ReadFromDisk();
UpdateStatus(1, MAX_STEPS, "Deserializing data...");
if (serializedData == null) return;
DeserializeData(serializedData);
UpdateStatus(2, MAX_STEPS, "Finished!");
}
private byte[] ReadFromDisk()
{
byte[] serializedData = null;
using (FileStream stream = new FileStream(_file, FileMode.Open, FileAccess.Read))
{
using (BufferedStream bufferedStream = new BufferedStream(stream))
{
BinaryFormatter formatter = new BinaryFormatter();
serializedData = formatter.Deserialize(bufferedStream) as byte[];
}
}
return serializedData;
}
private void DeserializeData(byte[] serializedData)
{
List<Data> dataList = SerializationHelper.Deserialize(serializedData, new List<Data>());
dataList.Clear();
dataList = null;
serializedData = null;
}
}
}
DataWriter :
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializationTesting
{
internal class DataWriter : DataIOBase
{
#region Declarations
private string _file;
private int _count;
#endregion
public DataWriter(string file, int count)
{
_file = file;
_count = count;
}
protected override void DoWork()
{
int maxSteps = _count + 2;
UpdateStatus(0, maxSteps, "Creating data...");
List<Data> dataList = CreateData(maxSteps);
UpdateStatus(_count, maxSteps, "Serializing data...");
byte[] serializedData = SerializationHelper.Serialize(dataList, null);
UpdateStatus(_count + 1, maxSteps, "Writing to file...");
WriteToDisk(serializedData, maxSteps);
serializedData = null;
dataList.Clear();
dataList = null;
UpdateStatus(maxSteps, maxSteps, "Finished!");
}
private List<Data> CreateData(int maxSteps)
{
List<Data> dataList = new List<Data>();
for (int i = 0; i < _count; i++)
{
UpdateStatus(i, maxSteps, string.Format("Creating item {0}...", i + 1));
Data data = new Data();
dataList.Add(data);
}
return dataList;
}
private void WriteToDisk(byte[] serializedData, int maxSteps)
{
using (FileStream stream = new FileStream(_file, FileMode.Create, FileAccess.Write))
{
using (BufferedStream bufferedStream = new BufferedStream(stream))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(bufferedStream, serializedData);
}
}
}
}
}
DataIOBase :
using System.Diagnostics;
using System.Threading;
namespace SerializationTesting
{
internal abstract class DataIOBase
{
#region Declarations
private Thread _thread;
#endregion
#region Events
public event UpdateStatusHandler OnStatusChange;
public event ProcessCompleteHandler OnComplete;
#endregion
public void Start()
{
KillThread();
_thread = new Thread(new ThreadStart(ThreadBody));
_thread.Start();
}
private void KillThread()
{
if (_thread == null) return;
if (!_thread.IsAlive) return;
try
{
_thread.Abort();
}
catch { }
}
private void ThreadBody()
{
Stopwatch sw = new Stopwatch();
sw.Start();
try
{
DoWork();
}
catch { }
finally
{
sw.Stop();
if (OnComplete != null)
{
OnComplete(sw.ElapsedMilliseconds);
}
}
}
protected abstract void DoWork();
protected void UpdateStatus(int curr, int max, string status)
{
if (OnStatusChange == null) return;
OnStatusChange(curr, max, status);
}
}
}
:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace SerializationTesting
{
[Serializable]
public class Data : ISerializable
{
#region Static Members
private static readonly int COLLECTION_SIZE_MIN = 1;
private static readonly int COLLECTION_SIZE_MAX = 20;
private static Random _rand = null;
private static Random Rand
{
get
{
if (_rand == null)
{
_rand = new Random();
}
return _rand;
}
}
#endregion
#region Instance Properties
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Name3 { get; set; }
public string Name4 { get; set; }
public string Name5 { get; set; }
public int Num1 { get; set; }
public int Num2 { get; set; }
public int Num3 { get; set; }
public int Num4 { get; set; }
public int Num5 { get; set; }
public bool Bool1 { get; set; }
public bool Bool2 { get; set; }
public bool Bool3 { get; set; }
public bool Bool4 { get; set; }
public bool Bool5 { get; set; }
public Dictionary<int, Data> Collection { get; set; }
#endregion
public Data(bool createCollection = true)
{
Init(createCollection);
}
#region Init
private void Init(bool createCollection)
{
try
{
Name1 = CreateString();
Name2 = CreateString();
Name3 = CreateString();
Name4 = CreateString();
Name5 = CreateString();
Num1 = CreateInt();
Num2 = CreateInt();
Num3 = CreateInt();
Num4 = CreateInt();
Num5 = CreateInt();
Bool1 = CreateBool();
Bool2 = CreateBool();
Bool3 = CreateBool();
Bool4 = CreateBool();
Bool5 = CreateBool();
CreateCollection(createCollection);
}
catch { }
}
private string CreateString()
{
int length = Rand.Next(1, 31);
char[] value = new char[length];
for (int i = 0; i < length; i++)
{
int charValue = Rand.Next(48, 91);
value[i] = (char)i;
}
return new string(value);
}
private int CreateInt()
{
return Rand.Next(1, 11);
}
private bool CreateBool()
{
int value = Rand.Next(0, 2);
return value == 0 ? false : true;
}
private void CreateCollection(bool populateCollection)
{
Collection = new Dictionary<int, Data>();
if (!populateCollection) return;
int count = Rand.Next(COLLECTION_SIZE_MIN, COLLECTION_SIZE_MAX + 1);
for (int i = 0; i < count; i++)
{
Data data = new Data(false);
Collection.Add(i, data);
}
}
#endregion
#region Serialization
public Data(SerializationInfo info, StreamingContext context)
{
SerializationHelper sh = new SerializationHelper(info);
Name1 = sh.GetItem("Name1", string.Empty);
Name2 = sh.GetItem("Name2", string.Empty);
Name3 = sh.GetItem("Name3", string.Empty);
Name4 = sh.GetItem("Name4", string.Empty);
Name5 = sh.GetItem("Name5", string.Empty);
Num1 = sh.GetItem("Num1", -1);
Num2 = sh.GetItem("Num2", -1);
Num3 = sh.GetItem("Num3", -1);
Num4 = sh.GetItem("Num4", -1);
Num5 = sh.GetItem("Num5", -1);
Bool1 = sh.GetItem("Bool1", false);
Bool2 = sh.GetItem("Bool2", false);
Bool3 = sh.GetItem("Bool3", false);
Bool4 = sh.GetItem("Bool4", false);
Bool5 = sh.GetItem("Bool5", false);
Collection = sh.GetItem("Collection", new Dictionary<int, Data>());
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name1", Name1);
info.AddValue("Name2", Name2);
info.AddValue("Name3", Name3);
info.AddValue("Name4", Name4);
info.AddValue("Name5", Name5);
info.AddValue("Num1", Num1);
info.AddValue("Num2", Num2);
info.AddValue("Num3", Num3);
info.AddValue("Num4", Num4);
info.AddValue("Num5", Num5);
info.AddValue("Bool1", Bool1);
info.AddValue("Bool2", Bool2);
info.AddValue("Bool3", Bool3);
info.AddValue("Bool4", Bool4);
info.AddValue("Bool5", Bool5);
info.AddValue("Collection", Collection);
}
#endregion
}
}
SerializationHelper :
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializationTesting
{
public class SerializationHelper
{
#region Declarations
private SerializationInfo _serializationInfo;
#endregion
public SerializationHelper(SerializationInfo serializationInfo)
{
_serializationInfo = serializationInfo;
}
#region Get Item
public T GetItem<T>(string item, T defaultValue)
{
try
{
object value = _serializationInfo.GetValue(item, typeof(T));
return (T)value;
}
catch
{
return defaultValue;
}
}
#endregion
#region Binary Serialization
public static T Deserialize<T>(byte[] serializedObject, T defaultValue, SerializationBinder binder = null)
{
if (serializedObject == null) return defaultValue;
try
{
object deserializedObject;
BinaryFormatter binaryFormatter = new BinaryFormatter();
if (binder != null)
{
binaryFormatter.Binder = binder;
}
using (MemoryStream stream = new MemoryStream(serializedObject))
{
stream.Seek(0, 0);
deserializedObject = binaryFormatter.Deserialize(stream);
}
if (!(deserializedObject is T))
{
return defaultValue;
}
return (T)deserializedObject;
}
catch
{
return defaultValue;
}
}
public static byte[] Serialize(object o, byte[] defaultValue, SerializationBinder binder = null)
{
if (o == null) return null;
try
{
byte[] serializedObject;
BinaryFormatter binaryFormatter = new BinaryFormatter();
if (binder != null)
{
binaryFormatter.Binder = binder;
}
using (MemoryStream stream = new MemoryStream())
{
binaryFormatter.Serialize(stream, o);
serializedObject = stream.ToArray();
}
return serializedObject;
}
catch
{
return defaultValue;
}
}
#endregion
}
}
仅供参考,截至2020年11月,MS建议不要在代码中使用使用BinaryFormatter。
可以考虑使用JsonSerializer或XmlSerializer。有关更多信息,请参见BinaryFormatter安全性指南。