Multithreading: windows forms and synchronization contexts

Published Tue, Jun 23 2009 12:14

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!

Filed under: ,

Comments

# 9eFish said on Tuesday, June 23, 2009 8:33 PM

9efish.感谢你的文章 - Trackback from 9eFish

# LA.NET [EN] said on Wednesday, June 24, 2009 6:34 AM

In the last posts , we’ve taken a look at how synchronization contexts help marshal work across threads

# ASPInsiders said on Wednesday, June 24, 2009 7:17 AM

In the last posts , we’ve taken a look at how synchronization contexts help marshal work across threads.

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above:  

Search

This Blog

Tags

Community

Archives

Syndication

Email Notifications

News




  • View Luis Abreu's profile on LinkedIn


    Follow me at Twitter

    My books

    Silverlight 4.0: Curso Completo

    ASP.NET 4.0: Curso Completo

    Portuguese LINQ book cover

    Portuguese ASP.NET 3.5 book cover

    Portuguese ASP.NET AJAX book cover

    Portuguese ASP.NET AJAX book cover