Reputation: 19
I use the XmlSerializer of C# to save and load all models of my application to a file. Because these file operations can take some time, I want do this serialization/deserialization process in a different thread. To do this, I used a delegate and the "BeginInvoke" operation. The problem is that when all objects are deserialized that they are created by another thread and that all variables in these objects cannot be accessed by the GUI thread.
I know how to access an object from a different thread (by using the Dispatcher.invoke() method) but using this technique to access each variable is not an option in my application.
Is there a solution or easier way to serialize and deserialize objects in a different thread?
Edit: When all objects are deserialized, the model notifies the view to create UI objects from the deserialized models.
//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.
}));
}
Upvotes: 1
Views: 1624
Reputation: 51284
[Edit]
Since you've added the exception ("The calling thread cannot access this object because a different thread owns it") in the comment below, it's now clear that GUI controls are being accessed from a non GUI thread. If you search for this on StackOverflow, you will see that it's the direct consequence of refusing to use Dispatcher.Invoke
.
I am afraid that you can do only two things:
The preferred way: Use Dispatcher.Invoke
to invoke the async deserialization callback on a GUI thread (I realize you've said it's not an option, but it's actually the best way to do it). Remember that Dispatcher.Invoke
doesn't access deserialized objects in any special way, it only invokes the callback handler on a GUI thread:
// your async callback
public void ObjectWasDeserialized(IAsyncResult result)
{
_dispatcher.Invoke(new Action<IAsyncResult>(UpdateSomeControl), result);
}
Don't use async callbacks at all, but a Future
pattern as described below. But that forces you to poll the result from a GUI thread, which is a bad thing.
What you probably need is a thread-safe way to signal the GUI thread that an object is ready to be accessed. That can be implemented as a Future
object (as explained in this article by Ayende).
Your background thread should create a wrapper around your object, which also contains a ManualResetEvent
set to false. After the object has been deserialized successfully, it should signal the event (ManualResetEvent.Set()
). UI thread which wants to access the object will have to do it through a property which blocks on this same event until it's signaled. That's logical, since you cannot guarantee that the object will be ready anytime the GUI thread wishes it.
[Edit] Found the article with an implementation. First part is what I was talking about, and ends by defining the InThe
class. The part which follows, however, is not necessary at all, and I don't recommend it.
Using the code provided by Ayende, you would have something like:
// 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
Upvotes: 1
Reputation: 11108
Deserializing thread has to store objects in some well known to UI thread place. Remember You will have to come up with some synchronization mechanism between threads.
I think I know why You have problems (I read comment to other answer). You deserialize and then notice UI that object is ready. You probably do sth like
UIComponent.DeserializationFinished()
. In this method You modify the UI which is not allowed.
Upvotes: 1