by luisabreu via LA.NET [EN] on 6/23/2009 11:14:07 AM
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:
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!
Original Post: Multithreading: windows forms and synchronization contexts
The content of the postings is owned by the respective author. CSharpFeeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on CSharpFeeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.