In This Chapter
• Before .NET 4.5: Event-based Asynchrony
• Before .NET 4.5: Asynchronous Programming Model
• .NET 4.5: Introducing the Async Pattern
• Getting Started With Async/Await
• Asynchronous Lambda Expressions
• Asynchronous I/O File Operations in .NET 4.5
When you go to the restaurant, there are waiters and waitresses ready to serve your table. A waiter takes your order, brings the order to the kitchen, goes to serve another table, and then comes back to your table to bring you your meals. While waiting for you to finish, the waiter does similar operations for other patrons. So, the waiter does not stand at your table from when you arrive until you finish your meal before going to serve another table; if he did, the restaurant would need to hire one waiter per table to avoid the block of their activity, which is not practical. If you compare this real-world description with computer programming, the waiter is some code in your application. If this code must perform a long-running operation and you write such a code in the user interface (UI) thread or, more generally, in one thread, your code will act like a waiter that waits from the start to the end of the meal on a table and cannot do anything else in the meantime, thereby blocking the application activity. This is what happens when you write code in a synchronous approach; synchronous code performs one task at a time and the next task starts only when the previous one completes. To avoid blocking the application, you can use multithreading and instances of the Thread
class, described in Chapter 42, “Processes and Multithreading.” With multithreading, you can write code that performs a long-running operation on a separate thread and keep the application responsive. Threading is a way to write asynchronous code that developers have been using for a long time, along with two patterns: the Event-based Asynchronous Pattern and the Asynchronous Programming Model. But actually, asynchrony does not necessarily mean running on a background thread. Instead, it means that a task is executed in different moments. Threading is one way to achieve asynchrony, but it is quite complex and not always the best choice. For this reason, Microsoft introduced new libraries and new keywords to the Visual Basic and Visual C# languages to make asynchronous call easy. In this chapter, you get an overview of both the Event-based Asynchronous Pattern and the Asynchronous Programming Model; then you learn about the new Asynchronous Pattern, which is without a doubt the most important new feature in Visual Basic 2012. You will see how easily you can now write modern and responsive applications via asynchronous code.
Modern applications often need to perform complex computations or access resources through a network. Complex computations can become very long, a network resource might not be available, or the application might not scale well on the server. If the code that performs this kind of operation is running in the same thread as the caller, the thread gets blocked until all operations complete. If such a thread is the UI thread, the user interface becomes unresponsive and can no longer accept the user input until all operations have been completed. This type of approach is called synchronous because only one operation at a time is executed until all the processes are completed.
Having an unresponsive user interface is not acceptable in modern applications, so this is the place where asynchrony comes in. Asynchrony enables you to execute some pieces of code in a different thread or context, so that the caller thread never blocks. If this is the UI thread, the user interface remains responsive even if other operations are running. The other thread (or context) then tells the caller thread that an operation completed, regardless of the successful or unsuccessful result. The .NET Framework has been offering, for a long time, two thread-based approaches to asynchrony called Event-based Asynchrony and Asynchronous Programming Model in which you launch operations in a different thread and get notification of their completion via delegates. As you saw in Chapter 43, “Parallel Programming and Parallel LINQ,” the .NET Framework 4.0 introduced the Task Parallel Library and the concept of parallelism. TPL makes it easier to create applications capable of scaling long-running operations across all the available processors. TPL also makes applications faster and more responsive while executing complex tasks concurrently, but this all about concurrency, which is not the same as asynchrony. In this chapter, you first learn about the Event-based Asynchrony and the Asynchronous Programming Model to get started with the important concepts; then you start putting your hands on the new possibilities offered by the .NET Framework 4.5. By doing so, it will be easier for you to compare the old way to the new way and understand why you should migrate your exiting code to use the new patterns.
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 run on 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.
The Asynchronous Programming Model (APM) is still based on threading. In this model, an operation is launched on a separated thread via a method whose name starts with Begin
(e.g. BeginWrite
). A method like this must accept, among its parameters, an argument of type IAsyncResult
. This is a special type used to store the result and the state of an asynchronous operation. The most important members of this interface are two properties: AsyncState
(of type Object
), which represents the result of the operation under the form of either a primitive or a composite type, and IsCompleted
(of type Boolean
), which returns if the operation actually completed. As another parameter, these methods must receive a delegate that will be executed when the asynchronous operation is completing. Within this delegate, you will be able to analyze the result of the asynchronous operation, but you will also need to explicitly end the asynchronous operation by invoking a method whose name starts with End
(e.g. EndWrite
). Some classes in the .NET Framework are built to be APM-ready, such as Stream
and its derived classes. So, to demonstrate how APM works, we can consider an example based on the FileStream
class. The following code demonstrates how to write some bytes to a stream asynchronously and how the callback receives information from the caller with IAsyncResult
.
Private Sub OpenStreamAsync()
Dim someBytes(1000) As Byte
Dim randomGenerator As New Random()
'Generate a random sequence of bytes
randomGenerator.NextBytes(someBytes)
Using fs As New FileStream("Somedata.dat", FileMode.Create, FileAccess.Write)
Dim result As IAsyncResult =
fs.BeginWrite(someBytes, 0, someBytes.Length,
AddressOf fs_EndWrite, fs)
End Using
End Sub
Private Sub fs_EndWrite(result As IAsyncResult)
Dim stream As FileStream = CType(result.AsyncState, FileStream)
stream.EndWrite(result)
'Additional work goes here...
End Sub
The IAsyncResult.AsyncState
property contains the actual data sent from the caller and must be explicitly converted into the type that you need to work with; in this case, the stream. The reason is that you also must explicitly invoke the EndWrite
method that finalizes the asynchronous operation. You can also pass custom objects as the IAsyncResult
argument for the callback, to pass more complex and detailed information that you might need to elaborate when the task completes.
The .NET Framework 4.5 introduces a new pattern that solves some problems related to threading and enables you to write better and cleaner code. It does this by introducing two new keywords: Async
and Await
. Async
is a modifier you use to mark methods that run asynchronous operations. Await
is an operator that gets a placeholder for the result of an asynchronous operation and waits for the result, which will be sent back at a later time while other operations are executed. This enables you to keep the caller thread responsive. For a first understanding of how this pattern works, let’s take a look at the following function that downloads the content of a website as a string, returning the result as an XDocument
that can be manipulated with LINQ:
Function DownloadSite() As XDocument
Dim client As New System.Net.WebClient
Dim content As String =
client.DownloadString("http://www.microsoft.com")
Dim document As XDocument = XDocument.Parse(content)
Return document
End Function
This code is pretty easy because it creates an instance of the WebClient
class, then downloads the content of the specified website, and finally returns the XML document converted through XDocument.Parse
. This code is synchronous, meaning that the caller thread will remain blocked until all the operations in the method body are completed. If the caller thread is the UI thread, the user interface will remain blocked. Figure 44.1 shows a graphical representation of how a synchronous call works.
This is how you can rewrite the previous code using the Async pattern:
Async Function DownloadSiteAsync() As Task(Of XDocument)
Dim client As New System.Net.WebClient
Dim content As String =
Await client.DownloadStringTaskAsync("http://www.microsoft.com")
Dim document As XDocument = XDocument.Parse(content)
Return document
End Function
Await as a Reserved Keyword
The Await
keyword is not a reserved word everywhere in the code. It is a reserved word when it appears inside a method or lambda marked with the Async
modifier and only if it appears after that modifier. In all other cases, it is not a reserved word.
This code is asynchronous, so it will never block the caller thread because not all the code is executed at the same time. Figure 44.2 shows a representation of an asynchronous call.
The Story of Threads with Async/Await
The Async
pattern relies on the concept of Task
described in the previous chapter. For this reason, asynchronous code written with Async
/Await
does not necessarily run on a separate thread. In fact, it is represented by an instance of the Task
class. Because one thread can run multiple Task
instances, it is normal that asynchronous code can run in the same caller thread, such as the UI thread.
The following is a list of important considerations that will be discussed in the next section:
• A method that runs asynchronous code must be marked with the Async
modifier. When the compiler encounters this modifier, it expects an Await
expression inside the method body.
• Methods marked with Async
are also referred to as asynchronous methods.
• By convention, names of asynchronous methods must end with the Async
suffix.
• Asynchronous methods must return a Task
(if they return no value) or a Task(Of T)
, where T is the type that you would return with synchronous methods. See the previous XDocument
example.
• Asynchronous methods support the standard access modifiers (such as Private
, Public
, and so on), but they cannot be iterator methods at the same time, so the Iterator
modifier cannot be used along with Async
.
• The Main
method of an application can never be asynchronous. Notice that if you mark the Main
method as asynchronous and write asynchronous calls in its body, the background compiler will not throw any warnings or exceptions. It will raise an error when you compile or try to run the code.
• Any method that returns a Task
or Task(Of T)
can be used along with Await
(“awaitable”).
• The Await
expression puts a placeholder for the result of the invoked task. The result will be returned later at some time, making the application remain responsive. However, the next code will actually run when the result has been returned. This is because the compiler ideally splits the method into two parts; the second part is nothing but a callback that is executed when the awaited task notifies the caller of its completion.
• Although you should return a Task
or Task(Of T)
, the compiler automatically infers the task-based type even if you return the original type. The second code snippet in the previous example demonstrates how the Return
statement returns XDocument
but the compiler automatically infers Task(Of XDocument)
.
Although these first important considerations might sound confusing, you will soon appreciate the benefits of using the Async
pattern. It enables you to avoid multithreading and explicit callbacks, enabling you to write much easier and cleaner code. In the next section, you get started with the Async
pattern with a practical example so that all the concepts described so far will be explained better.
The Async libraries are in the .NET Framework 4.5, so you can use the new pattern in whatever technology uses .NET 4.5. WPF, Windows Forms, ASP.NET, Silverlight, or Silverlight for Windows Phone. These are all technologies that can leverage the power of the new libraries. If you want to use Async with ASP.NET, remember to add an Async="true"
attribute in the XHTML code of the page where you are going to write asynchronous code:
<Page Async="True" .... />
In other words, you have no limits in using the new pattern and should always use this new way to asynchrony.
Async and Windows 8 Store Apps
There is another important reason beyond the availability of the Async pattern in .NET languages now: developing Windows 8 Store Apps. Windows 8 Apps require you to write most of the code asynchronously, and using old techniques would make developing apps really difficult. Instead, with the Async pattern, coding for Windows 8 is much faster, easier, and cleaner. Because the user interface of Windows 8 Apps must always be responsive with no exceptions, using the Async pattern is very common. This is another reason to read this chapter with particular attention.
Using the Async pattern enables you to write applications that are more responsive and that perform better, but you will not use it everywhere. In fact, there are specific situations in which Async has benefits. As a general rule, Async’s main purpose is to keep the UI responsive while tasks running in the UI thread might become potentially blocking. You use Async in the following scenarios:
• Potentially blocking tasks running in the user interface thread
• Working with images
• I/O operations (disk, networking, web access)
• Working with sockets
Using Async
and Await
differs from parallel programming because the purpose is not to have pieces of code that run concurrently; instead, the purpose is keeping the user interface responsive. In parallel programming, you have code that is CPU-consuming, so you use Task
instances to split the execution of your code into multiple units of work. Thus, most of the code is executed at the same time by using a multicore architecture. In Async, instead, you do not have CPU-consuming code. You might have potentially blocking tasks, though, so your goal is to keep the UI thread free. This is possible because the result of an Await
expression is delayed and control is yielded to the caller while waiting.
As usual, the best way to understand how a new technology works is through an example. In this section you see the Async pattern with an example based on retrieving information from the Internet. You will build a WPF application that downloads RSS feeds information from the Web, simulating a long-running process over a network. You first, though, create an application that works synchronously; then you see how to implement the Event-based Asynchronous Pattern described at the beginning of this chapter. Finally, you learn how things change in a third example built using the new Async
and Await
keywords.
Create a new WPF project with Visual Basic 2012 and .NET Framework 4.5. The application will consume the Open Data Protocol (OData) feed exposed by the Microsoft Research website, with particular regard to the list of published videos. Each item in the feed has a large number of properties, but for the sake of simplicity only the most important will be presented in the application’s UI. So, the first thing you need to do is create a class that represents a single video described in the feed. Listing 44.1 demonstrates how to implement a class called Video
.
Public Class Video
Public Property Title As String
Public Property Url As String
Public Property Thumbnail As String
Public Property DateRecorded As String
Public Property Speaker As String
Public Shared FeedUrl As String = _
"http://odata.research.microsoft.com/odata.svc/Videos/"
End Class
Notice that all the properties are of type String
just to represent values as they exactly come from the feed. Also, a shared field contains the feed URL. Because the code will use LINQ to XML to query the feed, you should add an XML schema via XML Schema Inference, which you learned in Chapter 27, “Manipulating XML Documents with LINQ and XML Literals.” Select Project, Add New Item and select the XML to Schema item template, as represented in Figure 44.3.
Call the new item ResearchXmlToSchema.xsd for consistency with the book. Now open the MainWindow.xaml file, to prepare the application’s user interface. The goal is to show the videos’ thumbnails and summary information and to provide the ability to click a thumbnail to open the video in its original location. The ListBox
control is a good choice to display a collection of items. This will be placed inside the default Grid
panel. Each item in the ListBox
will be presented via a custom template made of a Border and a StackPanel
that contains an Image
control (for the video thumbnail) and a number of TextBlock
controls that are bound to properties of the Video
class. Listing 44.2 shows the full code for the main window.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Name="VideoBox" ItemsSource="{Binding}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VirtualizingPanel.IsVirtualizing="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" Margin="5"
BorderThickness="2" Tag={Binding Url}
MouseLeftButtonUp="Border_MouseLeftButtonUp_1"
Width="200" Height="220">
<StackPanel>
<Image Source="{Binding Thumbnail}"
Width="160" Height="120" />
<TextBlock Text="{Binding Title}" TextWrapping="Wrap"
Grid.Row="1"/>
<TextBlock Text="{Binding DateRecorded}" Grid.Row="2"/>
<TextBlock Text="{Binding Speaker}" Grid.Row="3"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
It is worth mentioning that the code replaces the default items container (a VirtualizingStackPanel
) with a WrapPanel
container so that items are not forced to be presented on one line horizontally. This requires disabling the horizontal scrollbar on the ListBox
(ScrollViewer.HorizontalScrollBarVisibility="Disabled"
) and changing the ListBox.ItemsPanel
content with the WrapPanel
. Also notice how the Border.Tag
property is bound to the Url
property of the Video
class. This enables you to store the video’s URL and click the Border at runtime to open the video in its original location. Now switch to the code-behind file. The first thing you must do is add a number of Imports
directives, some for importing XML namespaces needed to map information from the RSS feed and some for working with additional .NET classes:
Imports <xmlns:xa="http://www.w3.org/2005/Atom">
Imports <xmlns:xd="http://schemas.microsoft.com/ado/2007/08/dataservices">
Imports <xmlns:xm="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
Imports System.Net
Imports System.Threading
The next step is implementing a method that queries the RSS feed returning the list of videos. In this first implementation, you will use a synchronous approach, which will block the user interface when the application is running:
Private Function QueryVideos() As IEnumerable(Of Video)
Dim client As New WebClient
Dim data As String = client.DownloadString(New Uri(Video.FeedUrl))
Dim doc As XDocument = XDocument.Parse(data)
Dim query = From video In doc...<xa:entry>
Select New Video With {
.Title = video.<xa:title>.Value,
.Speaker = "Speaker: " + video...<xd:MicrosoftHost>.Value,
.Thumbnail = video...<xd:Picture>.Value,
.Url = video...<xd:Url>.Value,
.DateRecorded = String.Concat("Recorded on ",
Date.Parse(video...<xd:DateRecorded>.Value).
ToShortDateString)
}
Return query
End Function
The code is simple. An instance of the WebClient
class, which provides simplified access to networked resources, is created and the invocation of its DownloadString
method downloads the entire content of the feed under the form of a String
object. Notice that this is the point at which the user interface gets blocked. In fact, it will need to wait for DownloadString
to complete the operation before returning to be responsive. After the feed has been downloaded, it is converted into an object of type XDocument
and a LINQ query enables you to retrieve all the needed information (refer to Chapter 27 for further information on LINQ to XML). Finally, a method called LoadVideos
will run the query and assign the result to the Window’s DataContext
; such a method will be invoked at startup. You can change this type of implementation, but it will be more useful later when making comparisons with the asynchronous implementation. The following code demonstrates this, plus the event handler for the MouseLeftButtonUp
event of the Border
control, where you launch the video in its original web page:
Private Sub LoadVideos()
Me.DataContext = QueryVideos()
End Sub
Private Sub MainWindow_Loaded(sender As Object,
e As RoutedEventArgs) Handles Me.Loaded
LoadVideos()
End Sub
Private Sub Border_MouseLeftButtonUp_1(sender As Object,
e As MouseButtonEventArgs)
'Tag is of type Object so an explicit conversion to String is required
Dim instance = CType(sender, Border)
Process.Start(CStr(instance.Tag))
End Sub
You can now run the application. Figure 44.4 shows the result of the query over the video feed.
The application works as expected, but the real problem with this approach is that the user cannot interact with the interface while the query is running. The reason is that the query’s code is running in the UI thread, so the user interface is busy with the query and does not accept any interaction. This can be easily demonstrated by attempting to move the window while the query is running because you will not be able to move it elsewhere. This has other implications: you cannot refresh controls that display the status of the task because they would be refreshed only when the query completes. Also, you cannot enable users to cancel the operation because you would need a button that the user would never be able to click.
A much better approach is moving the long-running operation into a separate thread, so that the UI can remain responsive while the other thread executes the operation. Lots of classes in the .NET Framework—especially those whose job is interacting with the Web and with networks-expose event-based asynchrony through methods that launch and execute an operation on a separate thread and raise an event when it is completed, passing the result to the caller via a callback. The WebClient
class has an asynchronous counterpart of DownloadString
, called DownloadStringAsync
, that you can use to run the query on a separate thread and wait for the query result via a callback. The following code demonstrates how to accomplish this (do not worry if you notice something wrong because an explanation is provided in moments):
Private Function QueryVideos() As IEnumerable(Of Video)
Dim client As New WebClient
Dim query As IEnumerable(Of Video)
AddHandler client.DownloadStringCompleted, Sub(sender, e)
If e.Error IsNot Nothing Then
'Error handling logic here..
End If
Dim doc = _
XDocument.Parse(e.Result)
query = From video In
doc...<xa:entry>
Select New Video With {
.Title = _
video.<xa:title>.Value,
.Speaker = "Speaker: " +_
video...<xd:MicrosoftHost>.Value,
.Thumbnail = _
video...<xd:Picture>.Value,
.Url = video...<xd:Url>.Value,
.DateRecorded = _
String.Concat("Recorded on ",
Date.Parse(video...<xd:DateRecorded>.Value).
ToShortDateString)
}
End Sub
client.DownloadStringAsync(New Uri(Video.FeedUrl))
Return query
End Function
The code specifies a statement lambda as an event handler for the DownloadStringCompleted
event, instead of declaring a separate delegate and pointing to this via an AddressOf
clause. The e
object is of type DownloadStringCompletedEventArgs
and contains the result of the operation. The problem in this code is that the Return
statement does not work because it is attempting to return a result that has not been yielded yet. On the other side, you cannot write something like this:
Private Function QueryVideos() As IEnumerable(Of Video)
Dim client As New WebClient
Dim query As IEnumerable(Of Video)
AddHandler client.DownloadStringCompleted, Sub(sender, e)
If e.Error IsNot Nothing Then
'Error handling logic here..
End If
Dim doc = _
XDocument.Parse(e.Result)
query = From ...
Return query
End Sub
client.DownloadStringAsync(New Uri(Video.FeedUrl))
End Function
This code does not work because you cannot return a result from a Sub
and because it should be returned from the outer method, not the inner. In conclusion, Return
statements do not work well with event-based asynchrony. The solution at this point is returning the result via a callback and an Action(Of T)
object. So the appropriate implementation of the QueryVideos
method in this approach is the following:
Private Sub QueryVideos(listOfVideos As Action(Of IEnumerable(Of Video),
Exception))
Dim client As New WebClient
AddHandler client.DownloadStringCompleted, Sub(sender, e)
If e.Error IsNot Nothing Then
listOfVideos(Nothing,
e.Error)
Return
End If
Dim doc = _
XDocument.Parse(e.Result)
query = From video In
doc...<xa:entry>
Select New Video With {
.Title = _
video.<xa:title>.Value,
.Speaker = "Speaker: " +_
video...<xd:MicrosoftHost>.Value,
.Thumbnail = _
video...<xd:Picture>.Value,
.Url = video...<xd:Url>.Value,
.DateRecorded = _
String.Concat("Recorded on ",
Date.Parse(video...<xd:DateRecorded>.Value).
ToShortDateString)
}
listOfVideos(query, Nothing)
End Sub
Try
client.DownloadStringAsync(New Uri(Video.FeedUrl))
Catch ex As Exception
listOfVideos(Nothing, ex)
End Try
End Sub
The previous code does the following:
1. Holds the list of videos from the RSS feed in an Action(Of IEnumerable(Of Video), Exception)
object. The Exception
instance here is useful to determine whether an error occurred during the query execution.
2. If the query completes successfully, the query result is passed to the Action
object (that is, the callback).
3. If an exception occurs during the query execution (see the first If
block inside the statement lambda), the callback receives Nothing
as the first parameter because the collection of items was not retrieved successfully and the exception instance as the second parameter.
4. If an exception occurs immediately when the web request is made, the callback still receives Nothing
and the exception instance. This is at the Try..Catch
block level.
So using a callback here has been necessary for two reasons: sending the query result back to the caller correctly and handling two exception scenarios. But you are not done yet. In fact, you have to completely rewrite the LoadVideos
method to hold the result of the callback and determine whether the operation completed successfully before assigning the query result to the Window’s DataContext
. The following code demonstrates this:
Private Sub LoadVideos()
Dim action As Action(Of IEnumerable(Of Video),
Exception) = Nothing
action =
Sub(videos, ex)
If ex IsNot Nothing Then
MessageBox.Show(ex.Message)
Return
End If
If (videos.Any) Then
Me.DataContext = videos
Else
QueryVideos(action)
End If
End Sub
QueryVideos(action)
End Sub
As you can see, the code is not easy, unless you are an expert. There is an invocation to the previous implementation of QueryVideos
, passing the instance of the callback. When the result is sent back, the statement lambda first checks for exceptions and, if not, takes the query result as the data source. If you now run the application again, you will get the same result shown in Figure 44.4; however, this time the user interface is responsive and the user can interact with it. But reaching this objective had costs. You had to completely rewrite method implementations and write code that is complex and difficult to read and to extend. So, the multithreading in this situation has not been very helpful. This is the point in which the Async/Await pattern comes in to make things simple.
The Async/Await
pattern has the goal of simplifying the way developers write asynchronous code. You will learn a lot about the underlying infrastructure, but before digging into that, it is important for you to see how your code can be much cleaner and readable. Let’s start by modifying the QueryVideos
method to make some important considerations:
Private Async Function QueryVideosAsync() As _
Task(Of IEnumerable(Of Video))
Dim client As New WebClient
Dim data = Await client.DownloadStringTaskAsync(New Uri(Video.FeedUrl))
Dim doc = XDocument.Parse(data)
Dim query = From video In doc...<xa:entry>
Select New Video With {
.Title = video.<xa:title>.Value,
.Speaker = String.Concat("Speaker: ",
video...<xd:MicrosoftHost>.Value),
.Thumbnail = video...<xd:Picture>.Value,
.Url = video...<xd:Url>.Value,
.DateRecorded = String.Concat("Recorded on ",
Date.Parse(video...<xd:DateRecorded>.Value).
ToShortDateString)}
Return query
End Function
Asynchronous methods must be decorated with the Async
modifier. When the compiler encounters this modifier, it expects that the method body contains one or more Await
statements. If not, it throws a warning saying that the method will be treated as synchronous, suggesting that the Async
modifier should be removed. Async methods must return an object of type Task
. If the method returns a value (Function
), then it must return a Task(Of T)
where T
is the actual result type. Otherwise, if the method returns no value, both following syntaxes are allowed:
Async Function TestAsync() As Task
'You can avoid Return statements, the compiler assumes returning no values
End Function
Async Sub TestAsync()
'...
End Sub
The difference between the two implementations is that the first one can be called inside another method with Await
, but the second one cannot (because it does not need to be awaited). A typical example of the second syntax is about event handlers: they can be asynchronous and can use Await
, but no other method will wait for their result. By convention, the suffix of async methods is the Async
literal. This is why QueryVideos
has been renamed into QueryVideosAsync
. An exception is represented by asynchronous methods already existing in previous versions of the .NET Framework, based on the EAP, whose name already ends with Async
. In this case Async
is replaced with TaskAsync
. For instance (as you discover in moments), the DownloadStringAsync
method in the WebClient
class has a new counterpart called DownloadStringTaskAsync
. Any method that returns a Task
or Task(Of T)
can be used with Await
. With Await
, a task is started but the control flow is immediately returned to the caller. The result of the task will not be returned immediately, but later and only when the task completes. But because the control flow immediately returns to the caller, the caller remains responsive. Await
can be thought as of a placeholder for the task’s result, which will be available after the awaited task completes. In the previous QueryVideosAsync
method, Await
starts the WebClient.DownloadStringTaskAsync
method and literally waits for its result but, while waiting, the control flow does not move to DownloadStringAsyncTask
, while it remains in the caller. Because in the current example the code is running in the UI thread, the user interface remains responsive because the requested task is being executed asynchronously.
In other words, what Await
actually does is sign up the rest of the method as a callback on the task, returning immediately. When the task that is being awaited completes, it will invoke the callback and will resume the execution from the exact point it was left.
After the operation has been completed, the rest of the code can elaborate the result. With this kind of approach, your method looks much simpler, like the first synchronous version, but it is running asynchronously with only three edits (the Async
modifier, Task(Of T)
as the return type, and the Await
operator).
Why the Task Type?
In Chapter 43 you learned a lot about the Task
class and saw how this can run CPU-intensive work on a separate thread, but it also can represent an I/O operation such as a network request. For this reason, the Task
class is the natural choice as the result type for asynchronous operations using Async
/Await
.
Continuing considerations on the previous method, take a look at the final Return
statement. It is returning an IEnumerable(Of Video)
, but actually the method’s signature requires returning a Task(Of IEnumerable(Of Video))
. This is possible because the compiler automatically makes Return
statements to return a Task
-based version of their result even if they do not. As a result, you will not get confused because you will write the same code but the compiler will take care of converting the return type into the appropriate type. This also makes migration of synchronous code to asynchronous easier. Continuing the migration of the code example to the Async/Await pattern, you now need a few edits to the LoadVideos
method. The following code demonstrates this:
Private Async Function LoadVideosAsync() As Task
Me.DataContext = Await QueryVideosAsync()
End Sub
The method is now called LoadVideoAsync
and marked with the Async
modifier. The reason is that it contains an Await
expression that invokes the QueryVideosAsync
method. The result of this invocation is taken as the main window’s data source. Finally, you have to edit the MainWindow_Loaded
event handler and make it asynchronous like this:
Private Async Sub MainWindow_Loaded(sender As Object,
e As RoutedEventArgs) Handles Me.Loaded
Await LoadVideosAsync()
End Sub
If you now run the application, you will still get a responsive user interface that you can interact with while the long-running task (the query) is executing, but you have achieved this by modifying existing code with very few edits.
Behind the scenes of the ease of the Async pattern, the compiler does incredible work to make the magic possible. When you make an asynchronous call by using Await
, that invocation starts a new instance of the Task
class. As you know from Chapter 43, one thread can contain multiple Task
instances. This means that it is the responsibility of the runtime to decide whether the new task must run in a separate thread or in the same calling thread. So you might have the asynchronous operation running in the same thread but on a new Task
. Internally, it’s as if the compiler could split an asynchronous method in two parts, a method and its callback. If you consider the QueryVideosAsync
shown previously, you could imagine a method defined until the invocation of Await
. The next part of the method is moved into a callback that is invoked after the awaited operation is completed. This has two benefits. The first benefit is that it ensures that code that needs to manipulate the result of an awaited operation will work with the actual result, which has been returned after completion of the task (this is in fact the moment in which the callback is invoked). Second, such callback is invoked in the same calling thread, which avoids the need of managing threads manually or using the Dispatcher
class in technologies like WPF or Silverlight. Figure 44.5 gives you an ideal representation of how the asynchronous method has been split.
Beyond considerations like the ones about threading, it is interesting to analyze the code the compiler generated to make asynchrony so efficient in Visual Basic 2012. For this exercise, you need a decompiler tool such as JustDecompile from Telerik, which ships for free and is available at http://www.telerik.com/products/decompiler.aspx. If you open the compiled .exe file with a tool like this, you can see that the implementation of asynchronous methods is completely different from the one you wrote and that the compiler generated several structures that implement a state machine that supports asynchrony. Figure 44.6 shows the QueryVideosAsync
real implementation.
For your convenience, the following is the auto-generated code for QueryVideosAsync
:
<AsyncStateMachine(GetType(VB$StateMachine_0_QueryVideosAsync))> _
<DebuggerStepThrough> _
Private Function QueryVideosAsync() As Task(Of IEnumerable(Of Video))
Dim asyncTaskMethodBuilder As AsyncTaskMethodBuilder(Of IEnumerable(Of Video)) =
AsyncTaskMethodBuilder(Of IEnumerable(Of Video)).Create()
Dim $sm As MainWindow.VB$StateMachine_0_QueryVideosAsync.$State = -1
$sm.$Builder = AsyncTaskMethodBuilder(Of IEnumerable(Of Video)).Create()
$sm.$Builder.Start(Of MainWindow.VB$StateMachine_0_QueryVideosAsync)($sm)
Dim task As Task(Of IEnumerable(Of Video)) = $sm.$Builder.Task
Return task
End Function
You do not need to know the code in detail because this implementation is purely internal; however, the real code relies on the AsyncTaskMethodBuilder
class, which creates an instance of an asynchronous method and requires specifying a state machine that controls the execution of the asynchronous task (which is returned once completed). For each asynchronous method, the compiler generated a Structure
object representing the state machine. For instance, the compiler generated a structure called VB$StateMachine_0_QueryVideosAsync
that represents the state machine that controls the execution of the QueryVideosAsync
method. Listing 44.3 contains the code of the aforementioned structure.
<CompilerGenerated> _
Private Structure VB$StateMachine_0_QueryVideosAsync
Implements IAsyncStateMachine
Friend $awaiter_0 As TaskAwaiter(Of String)
Friend $Builder As AsyncTaskMethodBuilder(Of IEnumerable(Of Video))
Friend $Stack As Object
Friend $State As Integer
Friend $VB$Me As MainWindow
Friend $VB$ResumableLocal_client$1 As WebClient
Friend $VB$ResumableLocal_data$1 As String
Friend $VB$ResumableLocal_doc$1 As XDocument
Friend $VB$ResumableLocal_query$1 As IEnumerable(Of Video)
<CompilerGenerated> _
Friend Overrides Sub MoveNext()
Dim $VB$ResumableLocalQuery$1 As IEnumerable(Of Video)
Dim awaiter As TaskAwaiter(Of String)
Dim flag As Boolean = False
Dim flag1 As Boolean = False
Try
If (Me.$State = 0) Then
Me.$State = -1
flag = True
End If
If (Not flag) Then
Me.$VB$ResumableLocal_client$1 = New WebClient()
awaiter = Me.$VB$ResumableLocal_client$1.
DownloadStringTaskAsync(New Uri(Video.FeedUrl)).GetAwaiter()
If (Not awaiter.IsCompleted) Then
Me.$State = 0
Me.$awaiter_0 = awaiter
Me.$Builder.AwaitUnsafeOnCompleted(Of TaskAwaiter(Of String),
MainWindow.VB$StateMachine_0_QueryVideosAsync)(awaiter, Me)
flag1 = True
End If
End If
If (flag OrElse Not flag1) Then
awaiter = New TaskAwaiter(Of String)()
Dim result As String = awaiter.GetResult()
Me.$VB$ResumableLocal_data$1 = result
Me.$VB$ResumableLocal_doc$1 =
XDocument.Parse(Me.$VB$ResumableLocal_data$1)
Me.$VB$ResumableLocal_query$1 = Me.$VB$ResumableLocal_doc$1.
Descendants(XName.Get("entry", "http://www.w3.org/2005/Atom")).
Select(Of XElement, Video)(Function(video As XElement)
Dim value As Video = New Video()
value.Title = InternalXmlHelper.Value
value.Speaker = String.Concat("Speaker: ", InternalXmlHelper.Value)
value.Thumbnail = InternalXmlHelper.Value
value.Url = InternalXmlHelper.Value
Dim dateTime As DateTime = DateTime.Parse(InternalXmlHelper.Value)
value.DateRecorded =
String.Concat("Recorded on ", dateTime.ToShortDateString())
Dim video1 As Video = value
End Function
)
$VB$ResumableLocalQuery$1 = Me.$VB$ResumableLocal_query$1
End If
Catch (exception1 As Exception)
Dim exception As Exception = exception1
ProjectData.SetProjectError(exception)
Dim $ex As Exception = exception
Me.$State = 1
Me.$Builder.SetException($ex)
ProjectData.ClearProjectError()
flag1 = True
End Try
If (Not flag1) Then
Me.$State = 1
Me.$Builder.SetResult($VB$ResumableLocalQuery$1)
End If
End Sub
<DebuggerNonUserCode> _
Private Overrides Sub System.Runtime.CompilerServices.IAsyncStateMachine.SetStateMachine(ByVal sm As IAsyncStateMachine)
Me.$Builder.SetStateMachine(sm)
End Sub
End Structure
The code in Listing 44.3 is certainly complex, and you are not required to know how it works under the hood, but focus for a moment on the MoveNext
method. This method is responsible of the asynchronous execution of tasks; depending on the state of the task, it resumes the execution at the appropriate point. You can see how the compiler translates the Await
keyword into an instance of the TaskAwaiter
structure, which is assigned with the result of the invocation to the Task.GetAwaiter
method (both are for compiler-use only). If you compare the result of this analysis with the ease of usage of the Async pattern, it is obvious that the compiler does tremendous work to translate that simplicity into a very efficient asynchronous mechanism.
Microsoft offers a lot of useful resources to developers who want to start coding the new way. The following list summarizes several resources that you are strongly encouraged to visit to make your learning of Async complete:
• Visual Studio Asynchronous Programming: The official developer center for Async. Here you can find documentation, downloads, instructional videos, and more on language specifications. It is available at http://msdn.microsoft.com/en-us/vstudio/async.aspx.
• 101 Async Samples: An online page that contains a huge number of code examples based on Async for both Visual Basic and Visual C#. You can find samples at http://www.wischik.com/lu/AsyncSilverlight/AsyncSamples.html.
• Sample code: Available on the MSDN Code Gallery (http://code.msdn.microsoft.com).
Do not leave out of your bookmarks the root page of the .NET Framework 4.5 documentation (http://msdn.microsoft.com/en-us/library/w0x726c2).
Another great benefit of using the Async pattern is that exception handling is done the usual way. In fact, if an awaited method throws an exception, this can be naturally handled within a Try..Catch..Finally
block. The following code provides an example:
Private Async Sub DownloadSomethingAsync()
Dim client As New System.Net.WebClient
Try
Dim result = Await client.
DownloadStringTaskAsync("http://msdn.com/vbasic")
Catch ex As Exception
Console.WriteLine(ex.Message)
Finally
Console.WriteLine("Operation completed.")
End Try
End Sub
As you can see, there is no difference in handling exceptions inside asynchronous methods compared to classic synchronous methods. This makes code migration easier.
As you remember from Chapter 43, the Task
class provides methods and other members that enable you to execute CPU-intensive work, by splitting code across all the available processors so that most of the code is executed concurrently, when possible. Such members of the Task
class return instances of the Task
class itself, and therefore can be used along with Await
. This possibility has some advantages:
• You can execute synchronous code on a separate thread more easily.
• You can run multiple tasks concurrently and wait for them to complete before making further manipulations.
• You can use Await with CPU-consuming code.
This approach is known as Task-based Asynchrony, and in this section you learn how to get the most out of it.
In Chapter 42 you learned how to write code that can run on a separate thread, how to create new threads manually, and how to use the Thread Pool managed by the .NET Framework. With this approach, you run a portion of synchronous code in a separate thread, thus keeping the caller thread-free from an intensive and potentially blocking work. In the .NET Framework 4.5, you have additional alternatives to reach the same objective but writing simpler code. The Task.Run
method enables you to run a new task asynchronously, queuing such a task into a thread in the Thread Pool. The result is returned as Task
handle for the intensive work, so that you can use Await
to wait for the result. Task.Run
takes as the first argument a delegate that defines the work that will be executed in the background thread. Such a delegate can be represented either by a method that you point to via the AddressOf
clause or by lambdas. In the latter case, the delegate can be a System.Action
represented by a statement lambda or a System.Func(Of T)
represented by a lambda expression. The following example demonstrates how synchronous code is easily executed in a separate thread by invoking Task.Run
:
Private Async Sub RunIntensiveWorkAsync()
'This runs on the UI thread
Console.WriteLine("Starting...")
'This runs on a Thread Pool thread
Dim result As Integer = Await Task.Run(Function()
Dim workResult As Integer = _
SimulateIntensiveWork()
Return workResult
End Function)
'This runs again on the UI thread
Console.WriteLine("Finished")
Console.ReadLine()
End Sub
Private Function SimulateIntensiveWork() As Integer
Dim delay As Integer = 5000
Threading.Thread.Sleep(delay)
Return delay
End Function
While the result of Task.Run
is being awaited, the control is immediately returned to the user interface, which remains responsive in the Console window. All the Console.WriteLine
and Console.ReadLine
statements are executed on the UI thread, whereas the simulated CPU-consuming code runs on the separate thread. Task.Run
schedules a new task exactly as Task.Factory.StartNew
does; you saw this method in Chapter 43. So, this code has the same effect as using Task.Run
:
Dim result As Integer = Await Task.Factory.StartNew(Function()
Dim workResult As Integer = _
SimulateIntensiveWork()
Return workResult
End Function)
In summary, Task.Run
lets you easily execute intensive computations on a separate thread, taking all the benefits of Await
.
The Task
class has other interesting usages, such as managing concurrent operations. This is possible because of two methods, Task.WhenAll
and Task.WhenAny
, also known as combinators. Task.WhenAll
creates a task that will complete when all the supplied tasks complete; Task.WhenAny
creates a task that will complete when at least one of the supplied tasks completes. For example, imagine you want to download multiple RSS feeds information from a website. Instead of using Await
against individual tasks to complete, you can use Task.WhenAll
to continue only after all tasks have completed. The following code provides an example of concurrent download of RSS feeds from the Microsoft Research OData feed used previously:
Private Async Sub DownloadAllFeedsAsync()
Dim feeds As New List(Of Uri) From
{New Uri("http://odata.research.microsoft.com/odata.svc/Videos/"),
New Uri("http://odata.research.microsoft.com/odata.svc/Downloads/"),
New Uri("http://odata.research.microsoft.com/odata.svc/Projects")}
'This task completes when all of the requests complete
Dim feedCompleted As IEnumerable(Of String) = _
Await Task.WhenAll(From feed In feeds
Select New System.Net.WebClient().
DownloadStringTaskAsync(feed))
'Additional work here...
End Sub
This code creates a collection of tasks by sending a DownloadStringTaskAsync
request for each feed address in the list of feeds. The task completes (and thus the result of awaiting WhenAll
is returned) only when all three feeds have been downloaded, meaning that the complete download result will not be available if only one or two feeds have been downloaded. WhenAny
works differently because it creates a task that completes when any of the tasks in a collection of tasks completes. The following code demonstrates how to rewrite the previous example using WhenAny
:
Private Async Sub DownloadFeedsAsync()
Dim feeds As New List(Of Uri) From
{New Uri("http://odata.research.microsoft.com/odata.svc/Videos/"),
New Uri("http://odata.research.microsoft.com/odata.svc/Downloads/"),
New Uri("http://odata.research.microsoft.com/odata.svc/Projects")}
'This task completes when any of the requests complete
Dim feedCompleted As Task(Of String) = Await Task.WhenAny(From feed In feeds
Select New System.Net.WebClient().
DownloadStringTaskAsync(feed))
'Additional work here...
End Sub
In this case a single result will be yielded because the task will be completed when any of the tasks completes. You can also wait for a list of tasks defined as explicit asynchronous methods, like in the following example:
Public Async Sub WhenAnyRedundancyAsync()
Dim messages As New List(Of Task(Of String)) From
{
GetMessage1Async(),
GetMessage2Async(),
GetMessage3Async()
}
Dim message = Await Task.WhenAny(messages)
Console.WriteLine(message.Result)
Console.ReadLine()
End Sub
Public Async Function GetMessage1Async() As Task(Of String)
Await Task.Delay(700)
Return "Hi VB guys!"
End Function
Public Async Function GetMessage2Async() As Task(Of String)
Await Task.Delay(600)
Return "Hi C# guys!"
End Function
Public Async Function GetMessage3Async() As Task(Of String)
Await Task.Delay(500)
Return "Hi F# guys!"
End Function
Here you have three asynchronous methods, each returning a string. The code builds a list of tasks including each asynchronous method in the list. Task.WhenAny
receives the instance of the collection of tasks as an argument and completes when one of the three methods completes. In this example, you are also seeing for the first time the Task.Delay
method. This is the asynchronous equivalent of Thread.Sleep
, but while the latter blocks the thread for the specified number of milliseconds, with Task.Delay
the thread remains responsive.
Additional Samples on Whenany
The 101 Async Samples include a couple of interesting examples of different usages of WhenAny
, such as interleaving one request at a time and limiting the number of concurrent downloads. You find them under the Combinators node of the samples page mentioned at the beginning of the chapter.
Because the Async pattern relies on the Task
class, implementing cancellation is something similar to what you have already studied back in Chapter 43, thus the CancellationTokenSource
and CancellationToken
classes are used. In this section you see how to implement cancellation both for asynchronous methods and for Task.Run
operations. Next, you learn how to report the progress of an operation, which is common in asynchronous programming and improves the user experience.
Let’s look at the WPF sample application created in the section called “Getting Started With Async/Await.” Imagine you want to give users the ability of cancelling the download of the RSS feed from the Microsoft Research website. First, make a slight modification to the user interface so that the main Grid
is divided into two rows, and in the first row add a Button
like this:
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Width="120" Height="30" Name="CancelButton"
Content="Cancel"/>
Do not forget to add the Grid.Row="1"
property assignment for the ListBox
control. Double-click the new button so that you can quickly access the code editor. Declare a new CancellationTokenSource
object that will listen for cancellation requests. The event handler for the new button’s Click
event will invoke the Cancel
method on the instance of the CancellationTokenSource
:
Private cancellationToken As CancellationTokenSource
Private Sub CancelButton_Click(sender As Object, e As RoutedEventArgs) _
Handles CancelButton.Click
If Me.cancellationToken IsNot Nothing Then
Me.cancellationToken.Cancel()
End If
End Sub
The user can now request cancellation by clicking this button. Next, you need to make a couple of edits to the QueryVideosAsync
method created previously. The first edit is making this method receive a CancellationToken
object as an argument. This object will handle cancellation requests during the method execution. The second edit requires replacing the WebClient
class with a new class called HttpClient
. The reason for this change is that the WebClient
’s asynchronous methods no longer support cancellation as in the first previews of the Async library, although asynchronous methods in System.Net.Http.HttpClient
do. Among the others, this class exposes a method called GetAsync
that retrieves contents from the specified URL and receives the cancellation token as the second argument. The result is returned under the form of a System.Net.Http.HttpResponseMessage
class. As the name implies, this class represents an HTTP response message including the status of the operation and the retrieved data. The data is represented by a property called Content
, which exposes methods to convert data into a stream (ReadAsStreamAsync
), into an array of bytes (ReadAsByteArrayAsync
), and into a string (ReadAsStringAsync
). Other than changing the code to use HttpClient
and to receive the cancellation token, you only need to handle the OperationCanceledException
, which is raised after the cancellation request is received by the asynchronous method. The following code demonstrates the QueryVideosAsync
method:
'The following implementation with HttpClient supports Cancellation
Private Async Function QueryVideosAsync(token As CancellationToken) As _
Task(Of IEnumerable(Of Video))
Try
Dim client As New HttpClient
'Get the feed content as an HttpResponseMessage
Dim data = Await client.GetAsync(New Uri(Video.FeedUrl), token)
'Parse the content into a String
Dim actualData = Await data.Content.ReadAsStringAsync
Dim doc = XDocument.Parse(actualData)
Dim query = From video In doc...<xa:entry>
Select New Video With {
.Title = video.<xa:title>.Value,
.Speaker = String.Concat("Speaker: ",
video...<xd:MicrosoftHost>.Value),
.Thumbnail = video...<xd:Picture>.Value,
.Url = video...<xd:Url>.Value,
.DateRecorded = String.Concat("Recorded on ",
Date.Parse(video...<xd:DateRecorded>.Value).
ToShortDateString)
}
Return query
Catch ex As OperationCanceledException
MessageBox.Show("Operation was canceled by the user.")
Return Nothing
Catch ex As Exception
MessageBox.Show(ex.Message)
Return Nothing
End Try
End Function
The very last edit to the application is changing the LoadVideosAsync
method to launch the query passing a cancellation token:
Private Async Function LoadVideosAsync() As Task
Me.cancellationToken = New CancellationTokenSource
Me.DataContext = Await QueryVideosAsync(Me.cancellationToken.Token)
End Function
If you now run the application, not only will the user interface remain responsive, but you will be also able to click the Cancel button to stop the query execution. Notice that in a synchronous approach, implementing cancellation has no benefits. In fact, if on one side writing code to support cancellation is legal, on the other side the user would never have a chance to click a button because the UI thread would be blocked until the completion of the task. Similarly, you can add cancellation to tasks running in a separate thread and started with Task.Run
. By continuing the example shown previously about this method, you can first rewrite the SimulateIntensiveWork
method as follows:
Private Function SimulateIntensiveWork(token As CancellationToken) _
As Integer
Dim delay As Integer = 5000
Threading.Thread.Sleep(delay)
If token.IsCancellationRequested Then
token.ThrowIfCancellationRequested()
End If
Return delay
End Function
You should be familiar with this approach because it has been discussed in Chapter 43. The method receives the cancellation token and checks for cancellation requests. If any exist, it throws an OperationCanceledException
. Next, you add support for cancellation by passing an instance of the CancellationTokenSource
class to the method invocation inside Task.Run
:
Private cancellationToken As CancellationTokenSource
Private Async Sub RunIntensiveWorkAsync()
cancellationToken = New CancellationTokenSource
'This runs on the UI thread
Console.WriteLine("Starting...")
Try
'This runs on a Thread Pool thread
Dim result As Integer = Await Task.Run(Function()
Dim workResult As Integer = _
SimulateIntensiveWork( _
cancellationToken.Token)
Return workResult
End Function)
'This runs again on the UI thread
Console.WriteLine("Finished")
Catch ex As OperationCanceledException
Console.WriteLine("Canceled by the user.")
Catch ex As Exception
End Try
Console.ReadLine()
End Sub
To request cancellation, you should call the cancellationToken.Cancel
method. At that point, the request is intercepted and an OperationCanceledException
is thrown.
Reporting the progress of an asynchronous method execution is a common requirement. There is a pattern that you can use and that makes things easier. This pattern relies on the System.IProgress(Of T)
interface and the System.Progress(Of T)
class, which expose a ProgressChanged
event that must be raised when the asynchronous operation is in progress. To provide an example that is easy to understand, imagine you still want to download the content of some feeds from the Microsoft Research website and refresh the progress every time a site has been downloaded completely. The current example is based on a Console application. Consider the following code:
Private progress As Progress(Of Integer)
Private counter As Integer = 0
Sub Main()
Try
progress = New Progress(Of Integer)
AddHandler progress.ProgressChanged, Sub(sender, e)
Console.
WriteLine("Download progress: " + _
CStr(e))
End Sub
DownloadAllFeedsAsync(progress)
Catch ex As Exception
Console.WriteLine(ex.Message)
Finally
Console.ReadLine()
End Try
End Sub
You first declare an object of type Progress(Of Integer)
and a counter. The first object will receive the progress value when the ProgressChanged
event is raised. In this case, the code uses the Integer
type to pass a simple number, but you can pass more complex information with a different or custom type. Then the code specifies a handler for the ProgressChanged
event, with type inference for the lambda’s parameters. Sender
is always Object
, whereas e
is of the same type as the generic type you assigned to Progress
. So, in this case it is of type Integer
. Here you are working in a Console application and are thus displaying the value as a text message. But in real-world applications, this is the value that you could assign to a ProgressBar
control to report the progress in the user interface. The instance of the Progress
class must be passed to the asynchronous method that performs the required tasks. The Progress
class has just one method called Report
; you invoke it after an Await
invocation. The following code demonstrates how to report the progress of downloading a number of feeds:
Private Async Sub DownloadAllFeedsAsync(currentProgress As IProgress(Of Integer))
Dim client As New System.Net.WebClient
Dim feeds As New List(Of Uri) From _
{New Uri("http://odata.research.microsoft.com/odata.svc/Videos/"),
New Uri("http://odata.research.microsoft.com/odata.svc/Downloads/"),
New Uri("http://odata.research.microsoft.com/odata.svc/Projects")}
For Each URL In feeds
Await client.DownloadStringTaskAsync(URL)
counter += 1
If currentProgress IsNot Nothing Then _
currentProgress.Report(counter)
Next
End Sub
Report
receives as an argument an object of the same type that you assigned as the generic argument of the Progress
class declaration in this case a counter of type Integer
that is incremented every time a feed is downloaded. If you run this code, every time a feed is returned, the progress is also shown in the user interface, as demonstrated in Figure 44.7.
This pattern makes reporting the progress of a task and in real-world applications, such as WPF and Windows 8 Apps, easy. It also makes updating controls like the ProgressBar
incredibly simple by assigning to such controls the value stored in the instance of the Progress
class.
Methods can be asynchronous, but so can lambda expressions. To be asynchronous, a lambda must have the Async
modifier and must return Task
or Task(Of T)
, but it cannot accept ByRef
arguments and cannot be an iterator function. A lambda can be asynchronous when its code uses the Await
operator to wait for a Task
result. An example of asynchronous lambdas is with event handlers. For instance, you might need to wait for the result of a task when an event is raised, as in the following code snippet that handles a button’s Click
:
AddHandler Me.Button1.Click, Async Sub(sender, e)
Await DoSomeWorkAsync
End Sub
You do not need an asynchronous lambda if the work you are going to execute does not return a Task
. Another typical usage of asynchronous lambdas is with Task.Run
. The following code shows the same example described when introducing Task.Run
, but now the lambda that starts the intensive work is marked with Async
and the method that actually performs intensive computations returns a Task
so that it can be awaited:
Private Async Sub RunIntensiveWorkAsync()
cancellationToken = New CancellationTokenSource
'This runs on the UI thread
Console.WriteLine("Starting...")
Try
'This runs on a Thread Pool thread
Dim result As Integer = Await Task.Run(Async Function()
Dim workResult As Integer = _
Await _
SimulateIntensiveWorkAsync()
Return workResult
End Function)
'This runs again on the UI thread
Console.WriteLine("Finished")
Catch ex As OperationCanceledException
Console.WriteLine("Canceled by the user.")
Catch ex As Exception
End Try
Console.ReadLine()
End Sub
Private Async Function SimulateIntensiveWorkAsync() As Task(Of Integer)
Dim delay As Integer = 1000
Await Task.Delay(delay)
Return delay
End Function
In previous versions of the .NET Framework, you could perform asynchronous operations over files and streams by using the Asynchronous Programming Model and methods such as Stream.BeginRead
and Stream.EndRead
. This kind of approach can be good, but it has the limitations described in the section called “Getting Started with Async/Await” in this chapter. The .NET Framework 4.5 simplifies asynchronous I/O operations by using the Async pattern and by implementing asynchronous versions of methods that work with files and stream to avoid blocking the main thread. Such methods are exposed by the Stream
, FileStream
, MemoryStream
, TextReader
, and TextWriter
classes that you saw in action back in Chapter 18, “Manipulating Files and Streams.” Table 44.1 summarizes the available asynchronous methods.
To see some of these methods in action, create a new WPF project. The user interface of this sample application will have to look like Figure 44.8.
That said, add the following controls in the designer:
1. Three buttons, named ReadTextButton
, CopyButton
, and WriteButton
. Then, set their Content
properties with Read text
, Copy files
, and Write Something
, respectively.
2. Four TextBox
controls, named ReadTextBox
, SourceTextBox
, DestinationTextBox
, and StatusTextBox
.
3. Three TextBlock
controls. You do not need to specify a name, but make sure their Text
property is set with Source folder
, Destination folder
, and Status
, respectively.
The first example uses the StreamReader
class to read a text file asynchronously. The event handler for the ReadTextButton.Click
event looks like this:
Private Async Sub ReadTextButton_Click(sender As Object,
e As RoutedEventArgs) _
Handles ReadTextButton.Click
Using reader As New StreamReader("TextFile1.txt")
Me.ReadTextBox.Text = Await reader.ReadToEndAsync
End Using
End Sub
You mark the event handler with Async
, and because this method will not be awaited by any other methods, it does not need to return a Task
. Therefore, it can be defined as a Sub
. Notice how you use Await
together with the ReadToEndAsync
method, while the rest of the implementation is made the usual way. The next example is about copying streams asynchronously. The following code shows the implementation of the CopyButton.Click
event handler:
Private Async Sub CopyButton_Click(sender As Object,
e As RoutedEventArgs) _
Handles CopyButton.Click
If Me.SourceTextBox.Text = "" Then
MessageBox.Show("Please specify the source folder")
Exit Sub
End If
If Me.DestinationTextBox.Text = "" Then
MessageBox.Show("Please specify the target folder")
Exit Sub
End If
For Each fileName As String In Directory.
EnumerateFiles(Me.SourceTextBox.Text)
Using SourceStream As FileStream = File.Open(fileName, FileMode.Open)
Using DestinationStream As FileStream =
File.Create(String.Concat(Me.DestinationTextBox.Text,
fileName.
Substring(fileName.LastIndexOf(""c))))
Await SourceStream.CopyToAsync(DestinationStream)
Me.StatusTextBox.Text = "Copied " + DestinationStream.Name
End Using
End Using
Next
End Sub
In particular, the code enumerates the content of the source folder and for each file it opens a stream for reading and another one for writing into the target folder. Await
enables you to execute asynchronously the operation with the asynchronous method called CopyToAsync
. It is worth mentioning that, with this approach, you can refresh the user interface with useful information, like showing the name of the last copied file. In a synchronous approach, this would not be possible because the UI would be blocked until the completion of the operation. The last example demonstrates how to write some text into a file asynchronously. This is the event handler for the WriteButton.Click
event:
Private Async Sub WriteButton_Click(sender As Object,
e As RoutedEventArgs) Handles WriteButton.Click
Dim uniencoding As UnicodeEncoding = New UnicodeEncoding()
Dim filename As String =
"c: empAsyncWriteDemo.txt"
Dim result As Byte() = uniencoding.GetBytes("Demo for Async I/O")
Using SourceStream As FileStream = File.Open(filename, FileMode.OpenOrCreate)
SourceStream.Seek(0, SeekOrigin.End)
Await SourceStream.WriteAsync(result, 0, result.Length)
End Using
End Sub
In this particular example, the code is written exactly like in the synchronous counterpart, except for the Async
modifier and the Await
statement that invokes the WriteAsync
method (instead of running Write
). By using the Async pattern, writing applications that work with streams and remain responsive has become dramatically simpler.
Implementing Custom Awaiters
By using the Task-based asynchrony, you can work with instances of the Task
class and use Await
when waiting for their results. Using Task.Run
and combinators will usually avoid the need to create custom types that can be used along with Await
. However, in some situations you might want to define your own awaitable types. Reasons for making this can be various for example, performing some work over a control in the user interface from within a background thread. To accomplish this, you define a Structure
that implements the INotifyCompletion
interface to expose a method called OnCompleted
; then you need a method called GetAwaiter
. The MSDN documentation does not provide enough information about building custom types that can be used with Await, but fortunately a blog post by Stephen Toub from Microsoft shows how to create one. You can find it at http://bit.ly/gwQD4S. There is also another blog post from the same author where he discusses how to implement a custom awaitable type to perform asynchronous operations with sockets; you can find it at http://bit.ly/t5hbM6.
Building applications that remain responsive whatever task they are executing is something that developers must take care of, especially from the user interface perspective. This chapter explained how to use asynchrony to build responsive applications by first discussing old-style programming models such as the Event-based Asynchronous Pattern and the Asynchronous Programming Model. Both provide techniques to write asynchronous code that runs on separate threads. However, both have some limitations, such as code complexity, issues in returning information to caller threads or functions, and managing errors effectively. Visual Basic 2012 introduces a new asynchronous pattern based on the Async
and Await
keywords, which enable you to keep the UI thread free and write code that is similar to the synchronous approach and is much easier to read and maintain. You invoke an asynchronous task, and then its result is returned some time later, but the control is immediately returned to the caller. When the result is available, an implicit callback enables you to consume the result of the asynchronous operation effectively. The Async pattern relies on the concept of task and on the Task
class, which means that asynchrony easily includes support for cancellation, progress, anonymous delegates, and concurrent operations. The .NET Framework 4.5 itself exposes built-in classes that use the new Async pattern for making asynchronous I/O operations much easier with particular regard to streams and network requests. The Async pattern and the new Async
/Await
keywords represent the most important new feature in Visual Basic 2012 and can be used across multiple platforms and presentation technologies.