- Overview of Asynchrony
- The Old-Fashioned Way: Event-Based Asynchrony
- The Old-Fashioned Way: The Asynchronous Programming Model
- The Modern Way: The Async Pattern
- Getting Started with Async/ Await
- Exception Handling in Async
- Implementing Task-Based Asynchrony
- Cancellation and Progress
- Asynchronous Lambda Expressions
- Asynchronous I/O File Operations in .NET 4.6
- Debugging Tasks
- Summary
The Old-Fashioned Way: Event-Based Asynchrony
More often than not, applications need to perform multiple tasks at one time, while still remaining responsive to user interaction. One of the possibilities offered by the .NET Framework since the early days is the Event-based Asynchronous Pattern (EAP). A class that adheres to this pattern implements a number of methods whose names terminate with the Async suffix and that execute some work on a different thread. Such methods mirror their synchronous counterparts, which instead block the caller thread. Also, for each of these asynchronous methods, there is an event whose name terminates with the Completed suffix and that is raised when the asynchronous operation completes. This way, the caller gets notification of the completion. Because the user might want to cancel an asynchronous operation at a certain point, classes adhering to the EAP must also implement methods whose names terminate with CancelAsync, each related to one of the asynchronous methods that actually performs the requested work. When such work is completed, a delegate will handle the operation result before control is sent back to the caller; this delegate is also known as callback. This pattern also requires classes to support cancellation and progress reporting. To understand how EAP works, let’s consider a simple example based on the System.Net.WebClient class, which enables you to access networks from client applications. Consider the following code:
Sub Main() Dim client As New System.Net.WebClient AddHandler client.DownloadStringCompleted, AddressOf client_DownloadStringCompleted client.DownloadStringAsync(New Uri("http://msdn.microsoft.com")) End Sub
A new instance of the WebClient class is created. To receive notification of completion, you must subscribe the DownloadStringCompleted event (assuming you will download a string, but other methods and related events are available) and supply a delegate that will be invoked when the event is raised. After you have subscribed the event, you can then invoke the desired method; in the current example, it’s the WebClient.DownloadStringAsync method that downloads contents from the specified URL as a string. If you write other lines of code after the invocation of DownloadStringAsync, these are not necessarily executed after the download operation has completed as it would instead happen in synchronous code. So, if you need to manipulate the result of an asynchronous operation, you must do it inside the callback, which is the delegate invoked after the completion event is raised. The following code provides an example:
Private Sub client_DownloadStringCompleted(sender As Object, e As DownloadStringCompletedEventArgs) If e.Error Is Nothing Then Console.WriteLine(XDocument.Parse(e.Result).ToString) Console.WriteLine("Done") End If End Sub
As you can see, the DownloadStringCompletedEventArgs class contains information about the result of the asynchronous operation. Usually, a specific class inherits from System.EventArgs and stores the result of an asynchronous operation, one per asynchronous method. You can check for errors, and if everything is successful, you can then work with the e.Result property that contains the actual result of the task. Classes that adhere to the EAP also enable you to report the progress of an asynchronous operation by exposing a ProgressChanged event. Continuing the previous example, the WebClient class exposes an event called ProgressChanged and a class called DownloadProgressChangedEventArgs that stores information about the operation progress. To handle such an event, you must first subscribe it like this:
AddHandler client.DownloadProgressChanged, AddressOf client_DownloadProgressChanged
You then handle the ProgressChanged event to report progress:
Private Sub client_DownloadProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs) Console.WriteLine(e.ProgressPercentage) 'Use e.BytesReceived for the number of bytes received in progress 'Use e.TotalBytesToReceive to get the total bytes to be downloaded End Sub
You can eventually use lambda expressions and statement lambdas as anonymous delegates, as demonstrated in the following code:
Private Sub Download() Dim client As New WebClient AddHandler client.DownloadStringCompleted, Sub(sender, e) If e.Error Is Nothing Then Console.WriteLine(XDocument. Parse(e.Result). ToString) End If End Sub client.DownloadStringAsync(New Uri("http://msdn.microsoft.com")) End Sub
The EAP has been very popular among developers for years because the way you write code is similar to how you handle events of the user interface. This certainly makes the asynchronous approach simpler. Later in this chapter, when comparing EAP to the new Async pattern, you will better understand why the old way can lead to confusion and become very complex to handle.