C# 5.0 Async-Feature: Be Aware Of The Synchronization-Context
Disclaimer: This post is based on the C# 5.0 CTP. Everything described here is subject to future changes.
The next version of C# and VB.NET will have support for asynchronous programming. In this post I don’t want to explain how it works and what you can do with it. For an introduction watch Anders Hejlsberg’s presentation from PDC 2010. There are more resources on the CTP-site like Channel 9 videos, white-papers, examples and the CTP-binaries so that you can try the new features yourself.
I just want to focus on a very small detail of the async-feature. I assume that you’ve already informed yourself and know the basic bits. Let’s get started. As we know we can write code like this with the new async feature:
Now the compiler turns this code it turned inside out. The first part before the await operator is executed normally. Then rest of the method is packed into a continuation object which captures the state and logic to run asynchronous. Then the download is started, the continuation is passed to download task and the method returns immediately. As soon as the download is finished, the rest of the code is continued. As Anders Hejlsberg showed in his demo, all our code is actually executed on the same thread! The async feature doesn’t do any multi threading by itself. For example when you write a simple desktop application and use the async stuff your code is executed on the GUI-thread. You don’t need to do any locking. Let’s run this example-code in a WPF-Application and watch the output:
The output is:
So our code is running on the same thread. This makes programming an GUI application so much easier. The async operations just work! Cool, right?
Oh rly? The Same Thread?
Ok, in reality it’s not that simple. We know that the async-feature is based on .NET 4.0 Tasks. (ok, actually on the right method-signatures, but that’s a detail). The compiler builds the continuation-state machines for us, packs those into tasks and finally composes those tasks. But how the hell does the system ensure that the code is executed on the right thread? For example on the GUI-thread? Well lets run our example code again. This time in a simple console application. The output is:
Oh, oh, the code after the ‘await’ is executed on a different thread! So something seem to be wrong.
The Synchronization Context
So why do all the WPF / Silverlight examples work? Think about the process again: The whole point of the await operator is that you can instrument your code to run asynchronous while preserving the flow of your logic. However at some lower level something needs to actually run asynchronously. After it completes it calls back into our code. But it cannot ‘magically’ run code on another thread. That’s where the synchronization context comes in. The synchronization context is an abstraction which represents the ‘right’ callback context. It has been around since .NET 2.0. For example in a GUI application the synchronization context is the message-pump. In a thread pool the synchronization context could be the thread pool itself. It really depends on the context of your application.
In a WPF, Winforms, Silverlight app the synchronization context is set up for the main thread and represents the message pump. When in our example the asynchronous operation completes, it passes the continuation to the synchronization context, which adds it to the message pump. At some point in time the continuation is picked up and executed on the GUI-thread.
Your Own Synchronization Context
You can provide your own synchronization context. Of course for most application this isn’t necessary. And it’s certainly not easy to get right. Anyway, how do we get the console-application to run the async stuff on the same thread? We write a simple synchronization context which is just enough for the example. It is a message-pump like in a GUI application.
First we implement the synchronization context. To make this example simple this class implements the message pump and the synchronization context:
Then we kick of the main application. We set up the synchronization context, add the regular code as initial the message to the message-pump and the start running it:
And now our console application works like the GUI application:
Conclusion
The async feature of C# and VB.NET is awesome. It really makes asynchronous programming so much easier. However don’t be fooled and believe that everything just works. I recommend you to try out the CTP and try to understand how it works.
Thanks for excellent atricle. 🙂