As more and more cores are the norm with PC’s (I also expect to have smartphones with multi-core CPUs soon), programming with multiple threads gets more and more important.
This blog post gives an introduction to a new C# feature for working asynchronously by comparing it to older technologies.
.NET has some history on async programming:
- With .NET 1.0 the async pattern was available to have a standard way to deal with long running methods.
- .NET 2.0 added the async component pattern or async event pattern that made it easier with Windows-based programs. The async pattern from .NET 1.0 had the disadvantage that a callback method that is invoked when the long running operation completes makes use of a background thread and thus it is necessary to switch to the UI thread that owns the synchronization context.
- With .NET 4 several enhancements have been done to write asynchronous methods. .NET 4 added a standard cancellation mechanism, the Task library and Parallel Lf deINQ.
C# so far only had the lock keyword to synchronize multiple threads. This changes with the next version of C# that offers async and await keywords.
Before showing the upcoming C# feature let’s compare this with older ways of programming asynchronously. The first sample shows downloading some data in a synchronous manner with the WebClient class. DownloadData can be used to get data from a server into an byte array.
private void OnSync(object sender, RoutedEventArgs e) { byte[] data = null; textStatus.Text = string.Empty; using (WebClient client = new WebClient()) { data = client.DownloadData(address); textStatus.Text = "Completed"; } }
Doing this from within a Windows application, the user interface is stalled while the synchronous method DownloadData waits until all the data is retrieved from the server.
To not block the UI, the data can be retrieved in an async manner. The second sample shows the use of the async pattern. This pattern defines a Begin and End prefix to the methods used for async calls and makes use of the IAsyncResult interface.
Contrary to the HttpWebRequest class, the WebClient class does not offer this pattern by itself. The HttpWebRequest class could be used to invoke the methods BeginGetResponse and EndGetResponse that follow the async pattern. To invoke any synchronous method in an async manner also a delegate can be used. This is shown in this sample.
For invoking the method WebClient.DownloadData a delegate that requires a string parameter and returns a byte array is defined with the Func<string, byte[]> delegtate. The delegate instance named download references a lambda expression that gets a string parameter and returns the result from the WebClient.DownloadData method. The implementation of the lambda expression is invoked asynchronously by calling the BeginInvoke method from the delegate instance. The first argument of this method defines the input data for the method that is invoked by the delegate. With this argument value the method that is referenced by the delegate is invoked from thread pool thread.
The second parameter is of type AsyncCallback that is void and requires an IAsyncResult parameter. This parameter defines the method that is invoked as soon as the asynchronous method completes. Invoking the method EndInvoke from the delegate instance, the result (in this case a byte[]) is returned.
Invoking methods and properties from UI elements cannot be done directly, as the method that is assigned to the AsyncCallback is running within a background thread. To switch back to the UI thread, with WPF the Dispatcher property that returns a DispatcherObject can be used. The Invoke method of the DispatcherObject requires a delegate with the first parameter. Here, the Action<string> delegate is used to pass a string to the method.
private void OnAsync1(object sender, RoutedEventArgs e) { textStatus.Text = string.Empty; byte[] data = null; using (WebClient client = new WebClient()) { Func<string, byte[]> download = addr => { return client.DownloadData(addr); }; download.BeginInvoke(address, new AsyncCallback(ar => { data = download.EndInvoke(ar); Dispatcher.Invoke( new Action<string>(s => textStatus.Text = s), "Completed"); }), null); } }
Because of this complexity in special with Windows applications where it is necessary to get back to the UI thread the async event pattern was introduced with .NET 2.0. This pattern defines that a async method is postfixed with the name Async and defines an event with the postfix Completed that is invoked as soon as the async mehtod finishes. The big advantage in contrast to the callback method with the async pattern is that the event handler that is assigned to the Completed event is invoked from a thread that has the synchronization context. With WPF or Windows Forms applications this is the UI thread.
Doing the same sample as before with the async event pattern methods from the WebClient class can be used directly as this class offers an implementation of this pattern. The DownloadDataCompleted event a lambda expression is assigned to get the result from the async call, and change that status information in a TextBlock directly.
private void OnAsync2(object sender, RoutedEventArgs e) { textStatus.Text = string.Empty; using (WebClient client = new WebClient()) { client.DownloadDataCompleted += (sender1, e1) => { byte[] data = e1.Result; textStatus.Text = "completed"; }; client.DownloadDataAsync(new Uri(address)); } }
With WPF and Windows Forms the async event pattern is a lot easier to deal with contrary to the async pattern. However, it is still very different to programming synchronously. Before the async method is invoked it must be defined what happens when the method is finished which can make it quiet complex reading such code.
A goal of the new C# keywords is to write asynchronous code in a way that is similar to programming synchronous code. This sample requires the use of the Visual Studio Async CTP and a reference to the AsyncCTPLibrary assembly. This assembly contains an extension method to WebClient named DownloadDataTaskAsync that returns Task<byte[]>. Instead of using the task that is returned from this method the await keyword is used that means we’re waiting on the result of the task that is of type byte[]. Waiting here with the await keyword does not block the thread. Instead, the UI thread can do some other actions and gets informed by a message when the task is completed, so it can continue with the result that is returned from DownloadTaskAsync.
private async void OnAsync3(object sender, RoutedEventArgs e) { textStatus.Text = string.Empty; using (WebClient client = new WebClient()) { byte[] data = await client.DownloadDataTaskAsync(address); textStatus.Text = "Completed"; } }
Programming asynchronously this way is very similar to programming synchronously. The order of the code is similar to synchronous programming and it still behaves asynchronously.
async and await are just language features. It was possible to do the same with the current version of the compiler, the code just reads more complex.
This is really a great feature of the next version of the C# compiler. Expect more information about this in my blog.
Christian
Comments