Before I got into the details of the problem I want to briefly describe what the SynchronizationContext class really does and what it’s main purpose really is in the first part of the article. From that perspective I’m going to show how the basic functionality of the SynchronizationContext class can be implemented for the .NET compact framework in the second part of the article..
The msdn library documentation states:
Provides the basic functionality for propagating a synchronization context in various synchronization models.
I must admit the first time when I read this definition I didn’t really get the key point behind the SynchronizationContext class. Detailed look into the implementation of SynchronizationContext and its base classes provided me the following information:
The SynchronizationContext class is a class belonging to the System.Threading namespace. The SynchronizationContext provides a model to make the communication between threads easier and more robust especially if multiple threading contexts/apartments such as “UI threading context” etc. are present.
To get a deeper understanding of the definition above I want to give you a short example. Imagine if you have a separate thread performing an intense calculation such as calculating the n-th Fibonacci number. When the separate thread has finished its long running operation you want to display the n-th Fibonacci number on a user interface. Normally (without using the SynchronizationContext class) you would need to do the following (or at least something similar):
private delegate void FibonacciResultDelegate(long fibonacciResult); private void MethodCalledByTheFibonacciThread(long fibonacciNumber) { if( fibonacciResultTextBox.InvokeRequired) { FibonacciResultDelegate fibonacciDelegate = MethodCalledByTheFibonacciThread; fibonacciDelegate.Invoke(this, new object[] { fibonacciNumber }); return; } fibonacciResultTextBox.Text = fibonacciNumber.ToString(); }
With the SynchronizationContext class we can invoke delegates in the context of a different thread. For the example above we could do the following:
public class FibonacciPresenter { private readonly Thread workerThread; private readonly SynchronizationContext context; private readonly IFibonacciView fibonacciView; public FibonacciWorker(IFibonacciView view) { fibonacciView = view; context = SynchronizationContext.Current; workerThread = new Thread(new ThreadStart(FibonacciCalc)); workerThread.Start(); } private void FibonacciCalc() { long result = CalculateFibonacciNumber( ... ); context.Post(new SendOrPostCallback(delegate(object state) { fibonacciView.DisplayResult(result); }), null); } // details omitted... }
In code line 13 we can see how the SynchronizationContext is retrieved. The SynchronizationContex.Current property points to the SynchronizationContext of the thread where the FibonacciWorker was created (in my example the FibonacciWorker would be created in a control). Then the FibonacciCalc method can post (asynchronous) or send (synchronous) a SendOrPostCallback delegate containing the “job” which needs to be marshaled over the SynchronizationContext. Therefore the line fibonacciView.DisplayResult(result) would be invoked on the UI thread which allows us to remove the “invoke required” code parts and directly set the fibonacci calculation result to the textbox text property.
We can briefly summarize that the purpose of the SynchronizationContext is to post (asynchronous) or send (synchronous) SendOrPostCallback delegates in the correct threading context which simplifies marshaling.
Additionally:
The call to SynchronizationContext.Current has to be done on the UI thread and after at least one control was created. This can be a problem in application startup jobs (e.g. during splash screen).
@Urs Enzler
You’re certainly right! There are a few tricky things when dealing with SynchronizationContext classes that I omitted in this article because I wanted to keep the article as short as possible 😉 But thanks for adding this additional information!
[…] Part I: Mimic SynchronizationContext behaviour on .NET CF […]