In his usual quiet and unpretentious way, Anders Hejlsberg stepped up on stage at Microsoft PDC yesterday – and proceeded to launch a revolution. Two new keywords added to the C# and VB.Net languages was all it took, but the impact of those changes will be felt by programmers for years to come. And not just programmers: unlike other changes made to the .Net languages across the versions, these new additions will produce tangible benefits for users.
The Status Quo
Anders started his presentation by noting a current trend in applications [watch]: they are becoming increasingly connected. We’ve had client-server applications for decades, but we’re now seeing more and more applications that have to call out to a number of different services and co-ordinate their responses. This brings a number of problems. First there’s the latency - the application having to wait as the requests and responses pass back and forth over the network. Then, if we’re not careful, the application appears to hang, unable to repaint the user-interface because it’s too busy waiting for the server to respond. Meanwhile, on the other side of the network, all the server’s threads are tied up, waiting for responses to come back from other servers, so it stops responding to incoming requests – and scalability plummets.
Now we all know what the answer is: asynchronous programming. Rather than tying up our main thread waiting for a response to come back, launch the request on another thread and set up some kind of event handler so that you can be notified when the response comes back, and then finish the work.
We have the answer, but it’s not a pretty one, as anybody who has programmed a serious client-server application in Silverlight will tell you. In Silverlight there is no way of calling the server synchronously: Silverlight forces you to call asynchronously so that the browser hosting your application can remain responsive. In fact, there’s no way of doing something as simple as showing a dialog synchronously: if the way your application continues is dependant on the users’ response, you have to move the continuation logic into an event handler attached to the dialog so that it can be executed once the user clicks their choice of button.
Programming like this requires you to turn your thinking inside out, because you have to prepare your response (in an event-handler or whatever) before you even make the request. And if in your response you’re going to make another asynchronous call, you have to prepare your response for that even before your first response. Anders gave a small example of that [watch]. Here’s a small method that queries the NetFlix web service. Nice straight-forward code:
Make that asynchronous and see what happens:
See how it’s turned inside out: in line 4 we prepare our what-happens-next by attaching a lambda to the DownloadStringCompleted event, and the call to download the string is the very last thing we do. Then notice that we can no longer return a value from the method: it isn’t available when the method returns. Instead we have to get the user to pass in an Action that we can call back when the result is ready. So we’re forced into changing all the places where we call the QueryMovies method too: the effects of this change ripple out through the code. As Anders said, “it becomes lambdas all the way down”.
No wonder we only do this stuff when absolutely necessary – and our customers pay a price, getting applications that become unresponsive when busy, and servers that are not as efficient as they could be. All because, for mere mortals at least, asynchronous programming is just too hard.
Not any longer.
Asynchronous Programming for the rest of us
Up on stage, Anders introduced his two new keywords, async and await [watch]. Here’s how the asynchronous version of QueryMovies looks in C# 5.0:
It’s identical to our nice straight-forward but sadly synchronous version, with the exception of four small changes (highlighed in orange). The first is the addition of the async keyword in front of the method definition. This tells the compiler that it’s going to have to work some magic on this method and make it return its result asynchronously. This produces the second change: the method now returns a Task<Movie> instead of just Movie – but notice how the return statement is unaltered – the compiler is taking care of turning the return value into a Task.
In case you’re wondering, Task is a class that was introduced in .Net 4.0 as part of the Task Parallel Library. It simply encapsulates a value that is going to become available at some point in the future (hence, why in some languages the equivalent concept is called a Future). Importantly, Task provides the ContinueWith that allows you to schedule an event handler to be called when that value is available.
The most significant change is the introduction of the await keyword in line 4, and the change to call DownloadStringTaskAsync. DownloadStringTaskAsync is just a version of DownloadString that, instead of returning string, returns Task<string>. Again, this Task<string> is something that will deliver a string sometime in the future. Adding the await keyword in front of the method call allows us to await that future moment, at which point the string will be popped out of its Task wrapper and the method will continue as normal.
But get this: it is not waiting by blocking the current thread. Under the covers the compiler takes everything in the method that comes after the await keyword, everything that depends on the value we’re waiting for, and packages it up into a lambda function – an event handler. This lambda function it attaches to the Task<string> returned by DownloadStringTaskAsync so that it can be run when the string becomes available.
There are very few restrictions on what the bodies of async methods can look like. All the usual control flow operations, if statements, foreach, while, etc. work. Best of all, exception handling works without needing any changes. The compiler takes care of all the twistedness of the asynchronicity, leaving you to think in straight lines.
All this means that, once C# 5.0 is here, there will be no excuses for applications that suffer white-outs while they do some work. And it means some cheap improvements to scalability on the server side. Anders showed an example of a server application that processes some RSS feeds over which he waved the async wand [watch]. The time needed to process the page dropped from about 1.25 seconds to 0.25 seconds – 5 times faster. Here’s a snippet of the code so you can see again how few changes are needed:
This is big news. There are other languages that can already do this kind of thing, F# being one of them. But I don’t know of any other mainstream language that does it, or does it as neatly as this. Anders and team have created an incredibly powerful feature, but exposed it a way that everybody will be able to use.
You can play with this now. Anders announced a Visual Studio Async CTP which contains new versions of the C# and VB.Net compilers, and the necessary supporting APIs in the framework (including Async versions of lots of the existing APIs). It installs on top of Visual Studio 2010, and I gather that it enables Asynchronous methods in both .Net 4.0 and Silverlight.
- Whitepaper: Asynchrony in .Net: an excellent overview of the new features being added in the languages and framework.
- Eric Lippert’s blog: Eric is a member of the C# compiler team, and he’s running a series of posts on the design and implementation of the async feature.
- Jon Skeet’s blog: Jon has been digging into various details of the async implementation, and (as if Anders hasn’t already given us enough!) also speculating about further additions to C# 5.0 to supplement async support.
- Ian Griffiths has his own summary of the async functionality which digs into the exception handling side of it.
- C# Language Specification for Asynchronous Functions
- The C# team blog has an interesting Silverlight 4 Facebook API example.