C# 5 Async, Part 2: Asynchrony Today
The .NET Framework has always supported asynchronous operations. However, different mechanisms for supporting exist throughout the framework. While there are at least three separate asynchronous patterns used through the framework, only the latest is directly usable with the new Visual Studio Async CTP. Before delving into details on the new features, I will talk about existing asynchronous code, and demonstrate how to adapt it for use with the new pattern.
The first asynchronous pattern used in the .NET framework was the Asynchronous Programming Model (APM). This pattern was based around callbacks. A method is used to start the operation. It typically is named as BeginSomeOperation. This method is passed a callback defined as an AsyncCallback, and returns an object that implements IAsyncResult. Later, the IAsyncResult is used in a call to a method named EndSomeOperation, which blocks until completion and returns the value normally directly returned from the synchronous version of the operation. Often, the EndSomeOperation call would be called from the callback function passed, which allows you to write code that never blocks.
While this pattern works perfectly to prevent blocking, it can make quite confusing code, and be difficult to implement. For example, the sample code provided for FileStream’s BeginRead/EndRead methods is not simple to understand. In addition, implementing your own asynchronous methods requires creating an entire class just to implement the IAsyncResult.
Given the complexity of the APM, other options have been introduced in later versions of the framework. The next major pattern introduced was the Event-based Asynchronous Pattern (EAP). This provides a simpler pattern for asynchronous operations. It works by providing a method typically named SomeOperationAsync, which signals its completion via an event typically named SomeOperationCompleted.
The EAP provides a simpler model for asynchronous programming. It is much easier to understand and use, and far simpler to implement. Instead of requiring a custom class and callbacks, the standard event mechanism in C# is used directly. For example, the WebClient class uses this extensively. A method is used, such as DownloadDataAsync, and the results are returned via the DownloadDataCompleted event.
While the EAP is far simpler to understand and use than the APM, it is still not ideal. By separating your code into method calls and event handlers, the logic of your program gets more complex. It also typically loses the ability to block until the result is received, which is often useful. Blocking often requires writing the code to block by hand, which is error prone and adds complexity.
As a result, .NET 4 introduced a third major pattern for asynchronous programming. The Task<T> class introduced a new, simpler concept for asynchrony. Task and Task<T> effectively represent an operation that will complete at some point in the future. This is a perfect model for thinking about asynchronous code, and is the preferred model for all new code going forward. Task and Task<T> provide all of the advantages of both the APM and the EAP models – you have the ability to block on results (via Task.Wait() or Task<T>.Result), and you can stay completely asynchronous via the use of Task Continuations. In addition, the Task class provides a new model for task composition and error and cancelation handling. This is a far superior option to the previous asynchronous patterns.
The Visual Studio Async CTP extends the Task based asynchronous model, allowing it to be used in a much simpler manner. However, it requires the use of Task and Task<T> for all operations.