c#在不同线程中的序列化
本文关键字:序列化 线程 | 更新日期: 2023-09-27 18:03:59
我使用c#的XmlSerializer将应用程序的所有模型保存并加载到一个文件中。因为这些文件操作可能需要一些时间,所以我想在不同的线程中执行这个序列化/反序列化过程。为此,我使用了委托和"BeginInvoke"操作。问题是,当所有对象被反序列化时,它们是由另一个线程创建的,并且这些对象中的所有变量都不能被GUI线程访问。
我知道如何从不同的线程访问对象(通过使用Dispatcher.invoke()方法),但使用这种技术访问每个变量在我的应用程序中不是一个选项。
是否有一个解决方案或更简单的方法来序列化和反序列化对象在不同的线程?
编辑:当所有对象都被反序列化时,模型通知视图从反序列化的模型中创建UI对象。
//In the Model:
//when the deserialize operation is ready this handler is executed (not the GUI thread)
void ProjectLoaded(object sender, EventArgs e)
{
ProjectLoadedEventArgs projectLoadedEventArgs = e as ProjectLoadedEventArgs;
m_models = projectLoadedEventArgs.SerializedData.Models;
Notify(null);
}
//In the view:
public void Update(Object o)
{
model.Angle.... -> RIGHT VALUE!!!
Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
ResetCanvas();
model.Angle... -> ERROR: The calling thread cannot access this object because a different thread owns it.
}));
}
反序列化线程必须将对象存储在一些众所周知的UI线程位置。记住,你必须想出一些线程间的同步机制。
我想我知道为什么你有问题(我读了其他答案的评论)。你反序列化,然后通知UI对象准备好了。你可能会做某事UIComponent.DeserializationFinished()
。
[Edit]
既然你在下面的评论中添加了异常("调用线程不能访问这个对象,因为不同的线程拥有它"),现在很清楚,GUI控件正在从非GUI线程访问。如果你在StackOverflow上搜索这个,你会看到这是拒绝使用Dispatcher.Invoke
的直接后果。
恐怕你只能做两件事:
-
首选方式:使用
Dispatcher.Invoke
在GUI线程上调用异步反序列化回调(我意识到你说过这不是一个选项,但它实际上是最好的方法)。请记住,Dispatcher.Invoke
不以任何特殊方式访问反序列化对象,它只调用GUI线程上的回调处理程序:// your async callback public void ObjectWasDeserialized(IAsyncResult result) { _dispatcher.Invoke(new Action<IAsyncResult>(UpdateSomeControl), result); }
-
完全不要使用异步回调,而是使用下面描述的
Future
模式。但是,这迫使您从GUI线程轮询结果,这是一件不好的事情。
不管哪个线程创建了一个对象,它的数据仍然可以被其他线程访问。不建议从不同的线程访问GUI控件。
你可能需要的是一个线程安全的方式来通知GUI线程一个对象已经准备好被访问了。这可以作为Future
对象来实现(正如Ayende在本文中所解释的那样)。
你的后台线程应该在你的对象周围创建一个包装器,它也包含一个设置为false的ManualResetEvent
。在对象被成功反序列化之后,它应该向事件发出信号(ManualResetEvent.Set()
)。想要访问对象的UI线程将不得不通过一个属性来完成它,该属性阻塞在同一个事件上,直到它被标记。这是合乎逻辑的,因为您不能保证对象在GUI线程希望它的任何时候都准备好。
[Edit]找到有实现的文章。第一部分是我所讨论的内容,最后定义InThe
类。然而,下面的部分根本没有必要,我不建议这样做。
使用Ayende提供的代码,您将得到如下内容:
// a thread (it can even be a GUI thread) requests a future result
Future<SomeObject> future = InThe.Future<SomeObject>(() => Deserialize(file));
// later, in the GUI thread, you access the future wrapper directly
SomeObject result = future.Value; // this will block the calling thread until
// result is ready