我使用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.
}));
}
答案 0 :(得分:1)
反序列化线程必须将对象存储在UI线程位置的一些众所周知的位置。记住你必须在线程之间提出一些同步机制。
我想我知道你为什么会遇到问题(我读到其他答案的评论)。您反序列化,然后注意该对象已准备好的UI。你可能会喜欢
UIComponent.DeserializationFinished()
。在此方法中,您可以修改不允许的UI。
答案 1 :(得分:1)
[编辑]
由于您在下面的注释中添加了异常(“调用线程无法访问此对象,因为其他线程拥有它”),现在很清楚,GUI控件是从非GUI线程访问的。 If you search for this on 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
对象实现(如this article by Ayende中所述)。
你的后台线程应该在你的对象周围创建一个包装器,它还包含一个设置为false的ManualResetEvent
。成功反序列化对象后,它应该发出事件信号(ManualResetEvent.Set()
)。想要访问该对象的UI线程必须通过一个属性来执行它,该属性会阻止此相同的事件,直到它被发出信号。这是合乎逻辑的,因为你无法保证在GUI线程希望它的任何时候对象都准备就绪。
[编辑] 找到带有实现的文章。第一部分是我所说的,最后是定义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