Multithreading: windows forms and synchronization contexts
In the previous post, we’ve started looking at synchronization contexts. In this post, we’ll take a close look at the widows forms custom synchronization context. Whenever you run a windows form app, you might end up interacting with the WindowsFormsSynchronizationContext. The constructor of this class is responsible for getting a reference to the GUI thread so that it can then create a control which will be used for marshalling the results back to the UI thread.
With a valid reference to a control and after understanding how work is marshaled back to the GUI thread (which involves posting win32 messages to the UI thread – as we’ve seen in this post), it shouldn’t be a surprise to learn that the Post and Send methods are overridden and that the new implementation relies on the BeginInvoke and Invoke methods of the special “marshal” control. Here’s the current implementation copied from reflector:
public override void Post(
SendOrPostCallback d, object state) {
if (this.controlToSendTo != null) {
this.controlToSendTo.BeginInvoke(
d, new object[] { state });
}
}
public override void Send(
SendOrPostCallback d, object state) {
Thread destinationThread = this.DestinationThread;
if ((destinationThread == null) ||
!destinationThread.IsAlive) {
throw new InvalidAsynchronousStateException(
SR.GetString("ThreadNoLongerValid"));
}
if (this.controlToSendTo != null) {
this.controlToSendTo.Invoke(d, new object[] { state });
}
}
As you can see, the implementation performs several auxiliary verifications to ensure that everything is still ok before going on with the invocation of the methods.
You might be wondering how things get hooked up, ie, how is the WindowsFormsSyncrhonizationContext set up in the current thread’s ExecutionContext. The answer is rather simple: the base Control class performs that task from its constructor, ensuring that whenever you create any window, you end up with the correct synchronization context.
So, how can we use this information for building multithreaded code for GUIs? The first thing you should do is base your code in synchronization contexts. This ensures that you get the correct behavior and don’t need to worry about GUI threads (notice that this should work in other custom environments that have their own custom synchronization contexts). To show you how you can use this class in code, we’re going to update the code of one of the previous posts so that it uses synchronization contexts instead of relying in a control to marshal work back into the GUI thread:
var number = GetNumberFromSomewhere();
button1.Enabled = false;
//should check for null here!
var syncContext = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(state => {
var ctx = state as SynchronizationContext;
ctx.OperationStarted();
var isPrime = false;
Exception thrownException = null;
try {
//algorithm for checking if number is prime
}
catch (Exception ex) {
thrownException = ex;
}
finally {
ctx.Send(marshaledState => {
var result = marshaledState as PrimeVerifierResult;
button1.Enabled = true;
if (result.ThrownException != null) {
throw result.ThrownException;
}
if (result.IsPrime) {
MessageBox.Show("Prime number");
}
},
new PrimeVerifierResult(isPrime, thrownException));
ctx.OperationCompleted();
}
},
syncContext);
A couple of observations on the previous snippet:
- you should always check the context obtained from the Current property (though that is not done in this snippet);
- updating the button’s state needs to be done from the GUI thread. This is a peace of cake because we’ve already got a reference to the current SynchronizationContext (obtained on the GUI thread and passed to the secondary thread through the state parameter) and the only thing we need to do is call Send or Post;
- notice how we signal the beginning and ending of an operation by calling the OperationStarted and OperationCompleted methods. This allows us to notify the synchronous context that an asynchronous operation began so that it can perform any operation it sees fit.
Looking at the previous sample, you might think that it’s not as simple as the first one. And you’re right: if I was writing this code, I’d always go with option 1.
If instead of putting this code on the GUI, I told you that you’d need to write a a class that could be reused across several GUIs, then you can probably start seeing value in the previous code. In fact, if you look at it and pay enough attention, you can start to see that it looks a lot like the code we had when we implemented the EAP pattern: the main difference is that his code relies on a SynchronousContext instance while that old code used the AsyncOperation and AsyncOperationManager classes (btw, I’m talking about the internals here!).
The truth is that these classes are just helpers and in the next post we’ll see how they relate with synchronization contexts. Until then, stay tuned!