Asynchronous patterns - Build Windows® 8 Apps with Microsoft® Visual C#® and Visual Basic® Step by Step (2013)

Build Windows® 8 Apps with Microsoft® Visual C#® and Visual Basic® Step by Step (2013)

Chapter 8. Asynchronous patterns

After completing this chapter, you will be able to

§ Write code using the asynchronous pattern in WinRT.

§ Use the async and await keywords in C#.

§ Choose the right synchronization context in your code.

The preceding chapters showed how to write a Windows Store app, and you have already seen and written asynchronous code related to the Windows Runtime (WinRT) methods and events. This chapter explains how asynchronous calls work in WinRT and how to write your own asynchronous code correctly in application and library code. By reading this chapter, you will learn how to leverage the new asynchronous patterns available in .NET 4.5 and Windows Store applications.

await and async keywords for asynchronous patterns

In previous versions of Windows and .NET, many application programming interfaces (APIs) were exposed mainly through synchronous methods; rarely were asynchronous methods available. Thus, when you needed to implement asynchronous code, even while invoking methods available only synchronously, you had to write asynchronous wrappers and choose the right pattern at your own risk.

For example, to read the content of a text file in a string variable, you could have written this code in a traditional windows application in .NET.

void Operation()

{

string content;

using (StreamReader sr = new StreamReader("document.txt"))

{

content = sr.ReadToEnd();

}

DisplayContent(content);

}

Because the ReadToEnd call is synchronous, if the time required to access the file took a long time, and the code was embedded in an event connected to, for example, the click of a button or a menu item selection, the user interface of the entire application would have been frozen for the full duration required to read the content from the Document.txt file.

To avoid this issue, the code would ideally read the file in an asynchronous way in the background, returning control immediately to the application that handles the user interface, and avoiding problems. To do that, the method attached to the user interface event has to return as soon as possible. Performing an operation in an asynchronous manner means that the caller of a function does not need to wait for its completion before continuing, but will obtain the result of the completed operation later, after that operation has finished executing.

Because the function ReadToEnd did not expose an asynchronous pattern, to prevent the interface from freezing it was necessary to wrap the ReadToEnd call in a separate task that executed in a separate thread. This was the pattern required in .NET 4.0, which resulted in code similar to the following code:

void Operation()

{

Task.Factory.StartNew(

() =>

{

using (StreamReader sr = new StreamReader("document.txt"))

{

return sr.ReadToEnd();

}

}

).ContinueWith(

(t) => DisplayContent(t.Result)

);

}

However, .NET 4.5 libraries offer a simpler syntax for creating a Task object for traditional Windows applications. Each function that may result in a long response time offers an asynchronous version, which returns a Task<T> object. In .NET 4.5 the same code can be written as follows:

void Operation()

{

StreamReader sr = new StreamReader(@"document.txt");

Task<string> readTask = sr.ReadToEndAsync();

readTask.ContinueWith((t) => DisplayContent(t.Result));

}

NOTE

The code of the previous asynchronous example in .NET 4.5 is not functionally identical to the synchronous code. In fact, the using statement disappeared and the side effect is that the Document.txt file will be kept open much longer than required. To avoid that, you should write the following code:

void Operation()

{

StreamReader sr = new StreamReader(@"document.txt");

Task<string> readTask = sr.ReadToEndAsync();

readTask.ContinueWith(

(t) =>

{

sr.Close();

return t.Result;

}

).ContinueWith(

(t) => DisplayContent(t.Result)

);

}

This code is still not identical to the initial version, because if an exception occurs it doesn’t immediately close the file—that happens when the sr instance of the StreamReader class is collected by the garbage collector. For these reasons, it is important to use the asynchronous pattern based on the await keyword that you will see shortly.

To simplify the code and handle the using statement correctly (which was not included in the previous example), you can use the new async and await keywords available in C# 5.0. The await keyword executes the block of code that follows as a subsequent method call in a separate task, which is executed in a way similar to the ContinueWith call you have seen before. Because the method containing an await statement no longer executes all the lines of code before returning to the caller, it has to return a Task, which will have a completed result as soon as all the lines of the original method are executed. For this reason, it is marked with the async statement—transforming a void method into a method returning a Task object, whereas the async statement applied to a method returning a string would result in a method returning a Task<string>.

static async void Asynchronous2_Ok()

{

string content;

using (StreamReader sr = new StreamReader(@"document.txt"))

{

content = await sr.ReadToEndAsync();

}

DisplayContent(content);

}

The importance of await and async statements becomes evident when you realize that WinRT APIs offer only asynchronous versions of the API for each method that might have a response time higher than 50 milliseconds. In practice, any API performing an I/O operation either in an explicit or implicit way will result in this category, because the response time of an I/O operation is not always predictable.

Using async and await statements

In this procedure, you will use async and await statements to perform asynchronous operations that call the WinRT API to let a user select a file from a document library and then display its content.

1. Create a new Application project. To do that, open Visual Studio 2012, and from the File menu, select New Project (the sequence can be File | New | Project for full-featured versions of Visual Studio). Choose Visual C# in the Templates tree and then Windows Store from the list of installed templates, and then choose Blank App (XAML) project type from the list of available projects.

2. Select version 4.5 as the Microsoft .NET Framework target version for your new project. (This step is not necessary in the Visual Studio Express edition.)

3. Name the new project DisplayFile, and then choose a location on your file system without changing the default solution name. When you have finished, click OK.

As you saw in Chapter 3, the Windows Store Application template provides a default page (MainPage.xaml), an application entry point in the App class (App.xaml.cs), a default application description and a declaration in the Package.appxmanifest file, as well as four default images representing logos and a splash screen.

image with no caption

4. Scroll down the MainPage.xaml source code and insert a TextBox control and a Button control inside a StackPanel control, as illustrated in the bold lines of the following code excerpt:

5. <Page

6. x:Class="DisplayFile.MainPage"

7. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

8. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

9. xmlns:local="using:DisplayFile"

10. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

11. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

12. mc:Ignorable="d">

13.

14. <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

15. <StackPanel>

16. <Button Click="ChooseFile_Click" Content="Choose File" />

17. <TextBlock x:Name="Result" Height="600" />

18. </StackPanel>

19. </Grid>

</Page>

The TextBlock control will be filled with the content of the file selected by the user through the FileOpenPicker picker; the button will simply fire the code to start the picker, read the file in a string, and put it in the TextBlock.

20.Open MainPage.xaml.cs and add the method ChooseFile_Click, which implements the event handler for the button. You can also double-click the button in the Integrated Development Environment IDE designer. Add the async keyword to the method because it will call asynchronous methods using the .NET 4.5 pattern you saw earlier.

The following code represents the complete method definition.

private async void ChooseFile_Click(object sender, RoutedEventArgs e)

{

}

21.Add the following code to the method body to open the File Picker, retrieve the selected file, and display its content in the TextBlock.

22.var picker = new Windows.Storage.Pickers.FileOpenPicker();

23.picker.FileTypeFilter.Add("*");

24.var file = await picker.PickSingleFileAsync();

25.string content = await Windows.Storage.FileIO.ReadTextAsync(file);

this.Result.Text = content;

The first three lines of code create an instance of the FileOpenPicker class, as you saw in Chapter 6. In this case, the PickSingleFileAsync method is used in order to select just one file. This call is marked as async, so the remaining part of the method will be executed after the user selects a file, but ChooseFile_Click immediately returns control to its caller, which is the Windows message pump, so any other user interaction with this application will be handled correctly.

The value returned by PickSingleFileAsync is of type Task<Windows.Storage.StorageFile>, but because the await keyword was used, it can be used as a Windows.Storage.StorageFile type within a method marked with the async keyword. All the code required to get the value from theResult property of a Task instance and to create a new Task every time a new asynchronous call is performed is automatically created by the compiler thanks to the await and async keywords.

When a user has chosen a file, the code continues, reading the file content by calling the static method named Windows.Storage.FileIO.ReadTextAsync, which internally handles the required opening and closing of the file, removing the need to make the call within a using statement.

The complete code for MainPage.xaml.cs should look like the following:

using System;

using System.Threading.Tasks;

using Windows.System.Threading;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

namespace DisplayFile

{

public sealed partial class MainPage : Page

{

public MainPage()

{

this.InitializeComponent();

}

private async void ChooseFile_Click(object sender, RoutedEventArgs e)

{

var picker = new Windows.Storage.Pickers.FileOpenPicker();

picker.FileTypeFilter.Add("*");

var file = await picker.PickSingleFileAsync();

string content = await Windows.Storage.FileIO.ReadTextAsync(file);

this.Result.Text = content;

}

}

}

26.Run the application, choose a text file from your Documents folder and you will see its content on the screen. The following graphic shows the user interface for the main page of the application after we read a sample text document containing three lines.

image with no caption

Writing asynchronous methods

As you have seen in the previous section, handling an event in an asynchronous way is very simple. You just have to do two things: include the async keyword in the declaration of the method attached to the event, and inside the method body, write one or more await keywords corresponding to each call to other methods made through asynchronous patterns.

It is important to understand that the method for an event is always called in a synchronous way and does not release control to the message pump until the first call with await is executed. Thus, you have to evaluate whether you are executing code that could require a significant amount of time to be executed or not. You could consider that such a condition exists for any operation that might require more than 50 milliseconds to be completed, adopting the same metric used for WinRT APIs.

For example, suppose you need to make a calculation that could require a few seconds, such as the following LongCalculation method that simulates a long calculation by looping for the number of seconds specified in the parameter:

public static void LongCalculation(int seconds)

{

DateTime exitTime = DateTime.Now.AddSeconds(seconds);

while (DateTime.Now < exitTime) ;

}

If you call this method before any await call in an async method handling an event, the application will become unresponsive until code execution reaches the first await call. For example, consider what would happen if you call LongCalculation in the first line of the ChooseFile_Clickmethod of the previous example, resulting in the following version of code:

private async void ChooseFile_Click(object sender, RoutedEventArgs e)

{

LongCalculation(5);

var picker = new Windows.Storage.Pickers.FileOpenPicker();

picker.FileTypeFilter.Add("*");

var file = await picker.PickSingleFileAsync();

string content = await Windows.Storage.FileIO.ReadTextAsync(file);

this.Result.Text = content;

}

By running this code, you will see that when you click the Choose File button, the application will become unresponsive for five seconds (the value passed as parameter to LongCalculation). The user interface to choose the file will display only after that calculation completes. This is because the call to ChooseFile_Click is synchronous until execution encounters the first await, so it returns control to the message pump after calling the PickSingleFileAsync method.

To avoid such problems, you should write the LongCalculation method using the asynchronous pattern, so that you can call it with the await keyword in the ChooseFile_Click method. However, if you just add just the async keyword in the LongCalculation definition, as shown here:

public static async void LongCalculation(int seconds)

You get the following error when you try to call the LongCalculation method with an await keyword:

'DisplayFile.MainPage.LongCalculationAsync(int)' does not return a Task and cannot be awaited.

Consider changing it to return Task.

What you should understand from this is that the async keyword does not really make a method asynchronous—async is just a keyword that enables (and forces) the use of await within the method. In order to be called with await, a method must implement the following asynchronous pattern:

§ If the method is void, the asynchronous method must return a Task.

§ If the method returns a type T, the asynchronous method must return a Task<T>.

In other words, the three following methods:

public static void Sample1();

public static int Sample2();

public static string Sample3();

have the following corresponding asynchronous signatures:

public static async Task Sample1();

public static async Task<int> Sample2();

public static async Task<string> Sample3();

A simple way to transform a CPU-intensive function into an asynchronous one is to change its signature according to the previous pattern and to embed its body within a Task action, as in the following example:

public static async Task LongCalculationAsync(int seconds)

{

await Task.Factory.StartNew( () =>

{

DateTime exitTime = DateTime.Now.AddSeconds(seconds);

while (DateTime.Now < exitTime) ;

});

}

NOTE

The reason why an event method (such as the previous ChooseFile_Click one) does not need to return a Task is because it is not called through await; instead, it’s called using a fire-and-forget approach. In other words, there is no code waiting for the end of the asynchronous part of the event method.

However, remember that creating a new Task might execute code in a different thread, introducing a possible race condition caused by executing code in parallel threads. You will see how to synchronize execution of your code in the proper context later in this chapter. In general, if your code has slow response times because it is calling other APIs or libraries, always favor calling existing asynchronous versions of the methods of a library by using the await call, and only create new Task objects when you cannot rely on existing asynchronous methods.

Implementing asynchronous methods

In this procedure, you will implement an asynchronous method to avoid having the user interface become unresponsive while your code is executing a long-running operation.

1. Open the MainPage.xaml source code and add a ProgressBar control inside the existing StackPanel control, as illustrated in the bold line of the following code excerpt:

2. <Page

3. x:Class="DisplayFile.MainPage"

4. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

5. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

6. xmlns:local="using:DisplayFile"

7. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

8. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

9. mc:Ignorable="d">

10.

11. <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

12. <StackPanel>

13. <Button Click="ChooseFile_Click" Content="Choose File" />

14. <ProgressBar x:Name="Progress" HorizontalAlignment="Left"

15. Height="10" Width="1024"/>

16. <TextBlock x:Name="Result" Height="600" />

17. </StackPanel>

18. </Grid>

</Page>

The ProgressBar control will be updated through the code you add in the next step.

19.Open the MainPage.xaml.cs file and add the method InitializeProgressBar, which implements continuous update of the progress bar. This will be an indicator of the fact that the message pump is running and the application is responsive. If the message pump is blocked, the progress bar update will freeze for as long as the application is in an unresponsive state.

The following code is the complete method definition:

private void InitializeProgressBar()

{

var ui = System.Threading.SynchronizationContext.Current;

ThreadPoolTimer.CreatePeriodicTimer((timer) =>

{

ui.Post((a) =>

{

this.Progress.Value = (this.Progress.Value >= 100)

? 0 : this.Progress.Value + 1;

}, null);

}, new TimeSpan(0, 0, 0, 0, 100));

}

20.Change the MainPage constructor to add the call to the InitializeProgressBar method after the call to InitializeComponent. The following code is the complete method definition:

21.public MainPage()

22.{

23. this.InitializeComponent();

24. InitializeProgressBar();

}

25.Add the following method to the MainPage.xaml.cs file:

26.public static void LongCalculation(int seconds)

27.{

28. DateTime exitTime = DateTime.Now.AddSeconds(seconds);

29. while (DateTime.Now < exitTime) ;

}

30.Insert the call to LongCalculation as the first line of the ChooseFile_Click method.

31.private async void ChooseFile_Click(object sender, RoutedEventArgs e)

32.{

33. LongCalculation(5);

34. var picker = new Windows.Storage.Pickers.FileOpenPicker();

35. picker.FileTypeFilter.Add("*");

36. var file = await picker.PickSingleFileAsync();

37. string content = await Windows.Storage.FileIO.ReadTextAsync(file);

38. this.Result.Text = content;

}

39.Run the application and you will see that the progress bar continuously changes its state until you click the Choose File button, at which point the application will become unresponsive for five seconds and the progress bar will be frozen for those five seconds, resulting in a state similar to the following graphic.

image with no caption

40.After the five seconds elapses, you will see the user interface for selecting a file. At this point, you can close the application and apply the changes required to make an asynchronous call to LongCalculation discussed in the following steps.

41.Change the code in this way. First, rename the method to LongCalculationAsync, embed the code in a lambda expression passed to the Task.Factory.StartNew method, called by using the await keyword, and transform the method from void to async Task. Finally, put the await keyword before the LongCalculationAsync call. These changes are highlighted in bold in the following code:

42.using System;

43.using System.Threading.Tasks;

44.using Windows.System.Threading;

45.using Windows.UI.Xaml;

46.using Windows.UI.Xaml.Controls;

47.

48.namespace DisplayFile

49.{

50. public sealed partial class MainPage : Page

51. {

52. public MainPage()

53. {

54. this.InitializeComponent();

55. InitializeProgressBar();

56. }

57.

58. private void InitializeProgressBar()

59. {

60. var ui = System.Threading.SynchronizationContext.Current;

61. ThreadPoolTimer.CreatePeriodicTimer((timer) =>

62. {

63. ui.Post((a) =>

64. {

65. this.Progress.Value =

66. (this.Progress.Value >= 100) ? 0 : this.Progress.Value + 1;

67. }, null);

68.

69. }, new TimeSpan(0, 0, 0, 0, 100));

70. }

71.

72. private async void ChooseFile_Click(object sender, RoutedEventArgs e)

73. {

74. await LongCalculationAsync(5);

75. var picker = new Windows.Storage.Pickers.FileOpenPicker();

76. picker.FileTypeFilter.Add("*");

77. var file = await picker.PickSingleFileAsync();

78. string content = await Windows.Storage.FileIO.ReadTextAsync(file);

79. this.Result.Text = content;

80. }

81.

82. public static async Task LongCalculationAsync(int seconds)

83. {

84. await Task.Factory.StartNew( () =>

85. {

86. DateTime exitTime = DateTime.Now.AddSeconds(seconds);

87. while (DateTime.Now < exitTime) ;

88. } );

89. }

90. }

}

91.Run the application again. This time, after you click the Choose File button, the progress bar will continue to update during the five seconds you must wait before the file selection user interface appears. This is because the message pump is not blocked and the application is still responsive even though it’s executing a long operation before asking the user to select a file.

Wait for an event asynchronously

In the previous section, you saw how to call a long-running operation without blocking the responsiveness of the user interface of your application. However, this long operation was still called in a synchronous way in respect to the operation the user wanted to perform (pick a file). As you saw in the previous procedure, the file picker user interface displayed five seconds after the initial click. What do you have to do to perform such a long-running operation while still letting the user complete the file picker operation, without having to wait? To do that, you have to execute the operation in an asynchronous way without using the await keyword; instead directly manipulate the Task object returned by the asynchronous call you make.

In practice, if you save the result of an asynchronous call into a Task object, you can write an await statement targeting such an object in order to stop the code flow of a method until the corresponding asynchronous call has been terminated. In practice, if you write the following the code, theInputData call is made after the LongCalculationAsync completes, meaning the only reason to use await is to avoid the user interface becoming unresponsive.

await LongCalculationAsync(5);

InputData();

DisplayData();

However, suppose you move the await statement after the InputData call and before the line calling DisplayData, as shown in the following code.

Task longCalculation = LongCalculationAsync(5);

InputData();

await longCalculation;

DisplayData();

Now, the LongCalculationAsync call will be executed at the same time as InputData, and the DisplayData method will be called only after both LongCalculationAsync and InputData have completed.

Implementing asynchronous calls

In this procedure, you will implement an asynchronous call to an asynchronous method in order to execute parallel actions in your user interface without having to wait for a background action to complete.

1. Open the MainPage.xaml.cs file and locate the following call to LongCalculationAsync in the ChooseFile_Click method.

await LongCalculationAsync(5);

2. Remove the await keyword from that call, change the parameter from 5 to 40, and save the result returned from the call in a variable of type Task, so that the line becomes the following.

Task longTask = LongCalculationAsync(40);

3. In the ChooseFile_Click method you want to wait for longTask completion—after the file read operation but before displaying the content of the selected file in the textbox. To achieve such a wait, you use the await keyword, without using a call to one of the wait methods of the Taskclass (such as WaitOne), which might cause application deadlock. The resulting ChooseFile_Click method code should look like the following (changes made to the previous example are highlighted in bold).

4. private async void ChooseFile_Click(object sender, RoutedEventArgs e)

5. {

6. Task longTask = LongCalculationAsync(40);

7. var picker = new Windows.Storage.Pickers.FileOpenPicker();

8. picker.FileTypeFilter.Add("*");

9. var file = await picker.PickSingleFileAsync();

10. string content = await Windows.Storage.FileIO.ReadTextAsync(file);

11. await longTask;

12. this.Result.Text = content;

}

13.Run the application, click the Choose File button, and select a file. If you perform the file selection within 40 seconds and click Open within 40 seconds, you will wait before seeing the file content on screen. This is because the content will be displayed only after at least 40 seconds from the click of the Choose File button, waiting for the completion of the LongCalculationAsync call.

Handling exceptions in asynchronous code

If an exception occurs during an asynchronous call, it can be difficult to catch the exception properly because of the different threads involved in regular Task objects. However, the asynchronous pattern in WinRT and the await and async keywords automatically generate the wrapping code required to handle such exceptions in a simple way. You can continue writing your code with a sequential approach, without worrying about the need to place try/catch statements in the right place, because the compiler automatically generates the required code, such as in the following code example:

try

{

var picker = new Windows.Storage.Pickers.FileOpenPicker();

picker.FileTypeFilter.Add("*");

var file = await picker.PickSingleFileAsync();

string content = await Windows.Storage.FileIO.ReadTextAsync(file);

this.Result.Text = content;

}

catch( Exception ex )

{

// TODO – write exception handling code here

}

If an exception is thrown in an asynchronous call, it is propagated through the call chain even if different threads are involved. For example, if the PickSingleFileAsync returned a null value because the user canceled the operation, the following ReadTextAsync call would throw an exception because the first parameter would be null. To describe what happens internally, the ReadTextAsync call starts a new Task object and any exception thrown there is saved in the Task object and propagated to the caller of a waiting function. This would usually require several lines of code, but thanks to the await keyword the resulting code is almost the same as the code you would write for sequential calls.

What is important to know is that exceptions corresponding to the await call are thrown in your own code. Therefore, if you call an asynchronous method, save its result in a Task object, and then execute the await later in your code, all the lines you write between the asynchronous code and the await statement are executed regardless of whether the asynchronous call throws an exception. For example, consider the following code:

try

{

var file = await ReadFileAsync();

Task<string> processResult = ProcessFileAsync(file);

DisplayInfo();

string result = await processResult;

}

catch( Exception ex )

{

// TODO – write exception handling code here

}

The DisplayInfo method is always called and completed regardless of whether an exception is thrown during the asynchronous execution of the ProcessFileAsync call.

NOTE

You must be careful when you separate the await statement from the asynchronous call—save the task in a variable, such as the processResult variable in the previous example. Every time you do that, you need to be aware that an exception thrown in the asynchronous method might be hidden by subsequent exceptions thrown in methods that are executed before the awaitkeyword.

Handling exceptions thrown in asynchronous calls

In this procedure, you will handle exceptions thrown in an asynchronous call.

1. Open the MainPage.xaml.cs file, locate the ChooseFile_Click method, and then embed its code within a try/catch statement. Pass 5 as the parameter to the LongCalculationAsync call. The resulting code of the ChooseFile_Click method should look like the following (changes made to the previous example are highlighted in bold):

2. private async void ChooseFile_Click(object sender, RoutedEventArgs e)

3. {

4. try

5. {

6. Task longTask = LongCalculationAsync(5);

7. var picker = new Windows.Storage.Pickers.FileOpenPicker();

8. picker.FileTypeFilter.Add("*");

9. var file = await picker.PickSingleFileAsync();

10. string content = await Windows.Storage.FileIO.ReadTextAsync(file);

11. await longTask;

12. this.Result.Text = content;

13. }

14. catch( Exception ex )

15. {

16. this.Result.Text = ex.Message;

17. }

}

18.Locate the LongCalculationAsync method definition and add a line that throws an exception after the loop simulating a long calculation. The resulting code of the LongCalculationAsync method should be like the following (changes made to the previous example are highlighted in bold):

19.public static async Task LongCalculationAsync(int seconds)

20.{

21. await Task.Factory.StartNew( () =>

22. {

23. DateTime exitTime = DateTime.Now.AddSeconds(seconds);

24. while (DateTime.Now < exitTime) ;

25. } );

26. throw new Exception("Long Calculation Error");

}

27.Run the application, click the Choose File button, wait at least five seconds, and then select a file name. You will see that, instead of displaying the file content, the following message will be displayed:

Long Calculation Error

This is because, after five seconds, the LongCalculationAsync call completed its asynchronous execution by throwing an exception, which does not stop the PickSingleFileAsync and ReadTextAsync execution. In fact, the exception thrown by LongCalculationAsync has been saved in the corresponding Task and breaks the ChooseFile_Click execution only when the corresponding await statement is executed, which is the following line:

await longTask;

Thus, you can easily handle exceptions thrown by asynchronous calls—but you need to consider that if you separate await from asynchronous calls, an exception does not stop the code until the corresponding await executes.

Cancel asynchronous operations

When an asynchronous operation needs to be canceled, you need to communicate the cancellation request to code that might be executing in another thread. You can do this using a simple Boolean flag and having the part of the method called asynchronously poll the flag and exit from the running function when it finds that flag active. However, because a method can call other methods that in turn could run other asynchronous operations, you need a standard pattern that can transfer a cancellation request to inner asynchronous methods, so that a request for cancellation can propagate to inner asynchronous method calls and keep the latency between a cancel request and the execution break as short as possible.

The internal asynchronous interfaces used by WinRT are mapped to the standard Task Based Asynchronous pattern, which uses .NET classes such as CancellationToken and CancellationTokenSource to provide a way to cancel an asynchronous operation, propagating the request to any asynchronous call depth as needed. The basic idea is that you pass a CancellationToken object that contains the request for the cancellation, so that if an asynchronous method has to call another method in an asynchronous way, the same token is used and the cancellation automatically propagates through the call chain.

Because the standard asynchronous methods provided by .NET return an IAsyncInfo interface, in order to simplify the wrapping in a Task class containing the desired CancellationToken it is necessary to call the AsTask method passing the CancellationToken instance as a parameter. For example, if you want to provide a CancellationToken to the PickSingleFileAsync method, you have to convert this line:

var file = await picker.PickSingleFileAsync();

into the following:

var file = await picker.PickSingleFileAsync().AsTask(cancelPickSingleFile.Token);

where the cancelPickSingleFile instance has been declared in this way:

CancellationTokenSource cancelPickSingleFile = new CancellationTokenSource();

The cancelPickSingleFile can be used to cancel the asynchronous operation to which the token is assigned. The CancellationTokenSource class offers a Cancel method to forward the request for cancelling operation to the related asynchronous call.

Cancel operation in asynchronous calls

In this procedure, you will add an automatic cancellation of the file pick operation if the user does not make a selection within 30 seconds after clicking the Choose File button.

1. Open the MainPage.xaml.cs file and add the following method:

2. private static async void SetTimeoutOperation(int seconds, CancellationTokenSource cts)

3. {

4. await Task.Delay(seconds * 1000);

5. cts.Cancel();

}

6. Locate the ChooseFile_Click method, remove the call to LongCalculationAsync and its related await statement. Then add the declaration of a CancellationTokenSource that is passed as a parameter to the AsTask call over the PickSingleFileAsync result. The resulting code of theChooseFile_Click method should look like the following (changes made to the previous example are highlighted in bold):

7. private async void ChooseFile_Click(object sender, RoutedEventArgs e)

8. {

9. try

10. {

11. var picker = new Windows.Storage.Pickers.FileOpenPicker();

12. picker.FileTypeFilter.Add("*");

13.

14. CancellationTokenSource cancelPickSingleFile = new CancellationTokenSource();

15. SetTimeoutOperation(30, cancelPickSingleFile);

16.

17. var file = await picker.PickSingleFileAsync().AsTask(cancelPickSingleFile.Token);

18. string content = await Windows.Storage.FileIO.ReadTextAsync(file);

19. this.Result.Text = content;

20. }

21. catch (Exception ex)

22. {

23. this.Result.Text = ex.Message;

24. }

}

25.Run the application, click the Choose File button, and then wait until the user interface for picking a file disappears and the initial window appears again. It will display the following message:

A task was cancelled

The reason is that in order to cancel the asynchronous operation, a TaskCanceledException exception is thrown and it propagates to the catch statement in the ChooseFile_Click method. The exception emerges in the ChooseFile_Click method corresponding to the await call to thePickSingleFileAsync. This way, the following lines in the same try block (the call to ReadTextAsync and the assignment to the Result textbox) do not execute because control is transferred directly to the catch statement when the exception occurs.

You can cancel asynchronous operations made by WinRT classes by passing a CancellationToken to the Task obtained with AsTask method from a WinRT asynchronous call. The best way to generate and interact with a CancellationToken is to create a CancellationTokenSource, which is a class offering methods to request the cancellation of an operation bind to the corresponding CancellationToken instance.

Track operation progress

During the progress of an asynchronous operation, you might need to display the progress state. WinRT asynchronous calls can be wrapped in a Task object that offers the IProgress<T> interface that standardizes communicating state for an asynchronous operation in the Task Based Asynchronous pattern.

public interface IProgress<in T>

{

void Report( T Value );

}

By providing an object implementing IProgress to an asynchronous operation, it is possible to receive notifications about the state of the operation itself. All WinRT functions that return an IAsyncActionWithProgress<TProgress> object can be attached to code that displays the progress of the operation by using the AsTask syntax.

IProgress<TProgress> progress = ...;

await SomeMethodAsync().AsTask(progress);

The easiest way to obtain an object implementing the IProgress<T> interface is to create an instance of the Progress<T> class. For example, if you have a function that provides progress information through an int type, you have to pass a lambda function as parameter to the Progress<int>constructor that receives an integer as a parameter, such as in the following code:

IProgress<int> p = new Progress<int>( (value) =>

{

// Code that displays progress

// The value parameter is of type int

} );

The data type used in the progress interface depends on the asynchronous operation; you should refer to the WinRT documentation to determine the appropriate type for a specific method.

Track progress in asynchronous operation

In this procedure, you will add a method that simulates executing some work and reports the progress of the ongoing operation through the IProgress<T> interface.

1. Open the MainPage.xaml source code and add a Button control after the Choose File button, embedding the two buttons in a horizontal StackPanel. Next, add a ProgressBar named ProgressSomework after the TextBlock control, as illustrated in the bold lines of the following code excerpt:

2. <Page

3. x:Class="DisplayFile.MainPage"

4. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

5. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

6. xmlns:local="using:DisplayFile"

7. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

8. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

9. mc:Ignorable="d">

10.

11. <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

12. <StackPanel>

13. <StackPanel Orientation="Horizontal">

14. <Button Click="ChooseFile_Click" Content="Choose File" />

15. <Button Click="Start_Click" Content="Run Work" />

16. </StackPanel>

17. <ProgressBar x:Name="Progress"

18. HorizontalAlignment="Left" Height="10" Width="1024"/>

19. <TextBlock x:Name="Result" />

20. <ProgressBar x:Name="ProgressSomework"

21. HorizontalAlignment="Left" Height="10" Width="1024"/>

22. </StackPanel>

23. </Grid>

</Page>

The button displaying Run Work will call the Start_Click method, while the ProgressSomework progress bar will display the state of the ongoing operation.

24.Open the MainPage.xaml.cs file and add the method Start_Click, which implements the event handler for the Run Work button. You can also double-click the button in the IDE designer. Add the async keyword to the method because it will use the await statement.

The code here represents the method definition:

private async void Start_Click(object sender, RoutedEventArgs e)

{

}

25.Add the following code to the method to display that the loop is running and then finished, calling the asynchronous DoSomeWorkAsync method passing an object implementing IProgress<int>. In order to do that, create an instance of Progress<int> and pass a lambda expression that updates the value of the ProgressSomework progress bar according to the number received as parameter:

26.this.Result.Text = "Start running...";

27.await DoSomeWorkAsync(

28. new Progress<int>( (value) =>

29. {

30. this.ProgressSomework.Value = value;

31. } ));

this.Result.Text = "Loop finished";

The code in the lambda expression will be executed every time a progress notification will be sent from the asynchronous operation to the Progress instance. In this case, we will update the value of the progress bar named ProgressSomework every time a notification is received.

32.Add the method DoSomeworkAsync, which implements a dummy loop notifying progress after a short pause of 20 milliseconds for each iteration. The code here represents the complete method definition:

33.private async Task DoSomeWorkAsync(IProgress<int> progress) {

34. for (int i = 0; i <= 100; i++) {

35. if (progress != null) {

36. progress.Report(i);

37. }

38. await Task.Delay(20);

39. }

}

The call to the Report method in the progress object will execute the lambda expression passed as parameter to the Progress<int> constructor called in the previous step.

The complete code for MainPage.xaml.cs should look like the following listing:

using System;

using System.Threading;

using System.Threading.Tasks;

using Windows.System.Threading;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

namespace DisplayFile

{

public sealed partial class MainPage : Page

{

public MainPage()

{

this.InitializeComponent();

InitializeProgressBar();

}

private void InitializeProgressBar()

{

var ui = System.Threading.SynchronizationContext.Current;

ThreadPoolTimer.CreatePeriodicTimer((timer) =>

{

ui.Post((a) =>

{

this.Progress.Value = (this.Progress.Value >= 100)

? 0 : this.Progress.Value + 1;

}, null);

}, new TimeSpan(0, 0, 0, 0, 100));

}

private static async void SetTimeoutOperation(int seconds,

CancellationTokenSource cts)

{

await Task.Delay(seconds * 1000);

cts.Cancel();

}

private async void ChooseFile_Click(object sender, RoutedEventArgs e)

{

try

{

var picker = new Windows.Storage.Pickers.FileOpenPicker();

picker.FileTypeFilter.Add("*");

CancellationTokenSource cancelPickSingleFile =

new CancellationTokenSource();

SetTimeoutOperation(5, cancelPickSingleFile);

var file = await picker.PickSingleFileAsync().

AsTask(cancelPickSingleFile.Token);

string content = await Windows.Storage.FileIO.ReadTextAsync(file);

this.Result.Text = content;

}

catch (Exception ex)

{

this.Result.Text = ex.Message;

}

}

private async void Start_Click(object sender, RoutedEventArgs e)

{

this.Result.Text = "Start running...";

// Use Progress<T> instance for Progress info (same as WinRT/.NET Calls)

await DoSomeWork(

new Progress<int>((value) =>

{

this.ProgressSomework.Value = value;

} ));

this.Result.Text = "Loop finished";

}

private async Task DoSomeWorkAsync(IProgress<int> progress)

{

for (int i = 0; i <= 100; i++)

{

if (progress != null)

{

progress.Report(i);

}

await Task.Delay(20);

}

}

public async Task LongCalculationAsync(int seconds)

{

await Task.Factory.StartNew(() =>

{

DateTime exitTime = DateTime.Now.AddSeconds(seconds);

while (DateTime.Now < exitTime) ;

} );

}

}

}

40.Run the application and you will see that the first progress bar continuously changes its state. Then click Run Work. You will see that a “Start running...” message is displayed and the progress bar below this message starts updating, rising from 0 to 100 percent in about two seconds. After that, a “Loop finished” message will replace the “Start running...” message displayed initially. The resulting state should be similar to the following graphic.

image with no caption

The progress action is always executed in a safe execution context, allowing you to safely update the user interface. If you are used to asynchronous programming in .NET, you know that this type of synchronization with an object handling the user interface could be cumbersome in previous versions of .NET, but thanks to the Task Based Asynchronous pattern the code required to execute the code in a proper way is heavily reduced and simplified.

Synchronization with multiple asynchronous calls

When you need to make multiple asynchronous calls active at the same time, there are useful functions that can help in writing the code to wait for the end of the first call or to wait for all the pending calls.

For example, consider the following code:

await Operation1Async();

await Operation2Async();

await Operation3Async();

The total time required to execute the previous three lines of code is equal to the sum of the times required to execute each of the three functions. However, because the three operations are independent, it could be better to use the Task.WhenAll function to execute all three functions at the same time. This provides a best case, so that the time required to execute the Task.WhenAll method corresponds to the time required for the longest operation. The previous code can be written in this way:

var t1 = Operation1Async();

var t2 = Operation2Async();

var t3 = Operation3Async();

await Task.WhenAll( t1, t2, t3 );

It is also possible to avoid creating all the variables by putting the asynchronous calls directly in Task.WhenAll parameters.

await Task.WhenAll(

Operation1Async(),

Operation2Async(),

Operation3Async() );

Please note that for WinRT asynchronous calls it could be necessary to call the AsTask() method in order to obtain a valid Task object for Task.WhenAll or Task.WaitAny, which you will see shortly. Thus, if OperationXAsync were a WinRT function, the previous code would be the following.

await Task.WhenAll(

Operation1Async().AsTask(),

Operation2Async().AsTask(),

Operation3Async().AsTask() );

In a similar way, it is possible to wait only for the first task to be completed in a list of tasks, by using the Task.WhenAny function, which returns when the first call in the list has completed. In the following code, the firstCompleted variable will be assigned to the first completed task, which will correspond to t1, t2 or t3, depending on which call completed first.

var t1 = Operation1Async();

var t2 = Operation2Async();

var t3 = Operation3Async();

Task firstCompleted = await Task.WhenAny( t1, t2, t3 );

It is important to note that Task.WhenAll and Task.WhenAny should be used instead of WaitHandle, WaitAny, and WaitHandle.WaitAll methods, even when you have WaitHandle objects available. The reason is that the WaitHandle static methods ignore the need of using the proper synchronization context, which will be explained in the next section, and might block the current thread, resulting in a deadlock situation when such code is mixed with await statements.

Wait for multiple asynchronous calls executed in parallel

In this procedure, you will wait for the completion of all the asynchronous calls executed at the same time.

1. Open the MainPage.xaml source code, and add a Button control after the Run Work one within the same horizontal StackPanel, as illustrated in the bold line of the following code excerpt:

2. <Page

3. x:Class="DisplayFile.MainPage"

4. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

5. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

6. xmlns:local="using:DisplayFile"

7. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

8. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

9. mc:Ignorable="d">

10.

11. <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

12. <StackPanel>

13. <StackPanel Orientation="Horizontal">

14. <Button Click="ChooseFile_Click" Content="Choose File" />

15. <Button Click="Start_Click" Content="Run Work" />

16. <Button Click="WhenAll_Click" Content="WhenAll" />

17. </StackPanel>

18. <ProgressBar x:Name="Progress"

19. HorizontalAlignment="Left" Height="10" Width="1024"/>

20. <TextBlock x:Name="Result" />

21. <ProgressBar x:Name="ProgressSomework"

22. HorizontalAlignment="Left" Height="10" Width="1024"/>

23. </StackPanel>

24. </Grid>

</Page>

The button displaying WhenAll will call the WhenAll_Click method.

25.Also in the MainPage.xaml.cs file, add the following methods, which simulate three operations having different response times (1, 2, and 3 seconds, respectively):

26.private async Task Operation1Async() {

27. await Task.Delay(1000);

28.}

29.

30.private async Task Operation2Async() {

31. await Task.Delay(2000);

32.}

33.

34.private async Task Operation3Async() {

35. await Task.Delay(3000);

}

36.Add the method WhenAll_Click, which implements the event handler for the WhenAll button. You can double-click the button in the IDE (Integrated Development Environment) designer to create the method stub. Add the async keyword to the method because it will use the awaitstatement.

The following code shows the full method definition:

private async void WhenAll_Click(object sender, RoutedEventArgs e)

{

}

37.Add the following code to the method WhenAll_Click to execute the three asynchronous operation at the same time, waiting for all to complete and then displaying the elapsed time:

38.this.Result.Text = "WhenAll starting ...";

39.DateTime start = DateTime.Now;

40.await Task.WhenAll(

41. Operation1Async(),

42. Operation2Async(),

43. Operation3Async());

44.this.Result.Text = String.Format(

45. "WhenAll completed in {0} seconds",

(DateTime.Now - start).Seconds );

46.Run the application, click the WhenAll button, and then wait until the following message is displayed:

WhenAll completed in 3 seconds

The total time required for executing the three operations is three seconds, whereas it would have been six seconds if the three functions were executed sequentially (by using three distinct await statements).

Choose SynchronizationContext in libraries

The default behavior of the await statement is to capture the current SynchronizationContext and use it to synchronize the execution of the completion code (the code following the await statement) by using that context. This is the reason why it is not necessary to write synchronization code when manipulating user interface objects in asynchronous methods using the async and await keywords. However, although this behavior is very good in code that interacts directly with the user interface, it might not be such a good idea for a library that might be called by code that does not have to interact with the user interface.

The following statement:

await task;

will continue execution after task completion in the same execution context of the await call. In other words, if you have this method:

private async Task DoSomeworkAsync()

{

await OperationAsync();

OtherActivity();

}

the OtherActivity method will be called within the synchronization context of the initial DoSomeworkAsync method, while part of the OperationAsync call might be executed in a different synchronization context, for example in a newly created thread.

When you write code at the application level, this is usually the expected behavior. However, when you are writing a library, this behavior might not be optimal, for performance reasons. For example, if the DoSomeworkAsync method is called from a service that does not have a user interface, or the OtherActivity method does not have to interact with the user interface in any way, the default behavior that forces synchronization performs the operation more slowly than necessary. You can avoid such slowdowns by executing the OtherActivity method in a different thread than the one in which DoSomeworkAsync was initially called. By calling the ConfigureAwait method it is possible to change this behavior, asking for a called operation to execute the following code in the same thread used by the task that completed this activity. The ConfigureAwait method must be called as a method of the Task that should continue in the same thread, ignoring the existing SynchronizationContext when await has been called. The changes applied to the previous code are highlighted in the following example:

private async Task DoSomeworkAsync()

{

await OperationAsync().ConfigureAwait( false );

OtherActivity();

}

Summary

In this chapter, you have learned how to use asynchronous patterns in WinRT. The most important keywords are await and async, which automatically generate much of the required code to handle asynchronous calls and synchronization, reducing the number of threads required, and minimizing the need for synchronization with the user interface. You implemented an event in an asynchronous way, handled exceptions with asynchronous code, and cancelled pending asynchronous operations. You also displayed the progress of an asynchronous operation and optimized synchronization with multiple operations executed in parallel. Finally, you have seen how to correctly handle synchronization in code written for general purpose libraries.

Quick reference

To

Do This

Call asynchronous methods

Use the await keyword to make asynchronous calls within methods marked with the async keyword.

Handle exceptions thrown in asynchronous code

Use standard try/catch statements around code calling asynchronous methods using await keyword.

Cancel an asynchronous operation

Pass a CancellationToken to the asynchronous operation and call the Cancel method on the CancellationTokenSource.

Track the progress of asynchronous operation

Implement a IProgress<T> interface by creating an instance of Progress<T> passing the code that update the progress state.

Write asynchronous code in libraries (DLLs)

Consider using Task.ConfigureAwait( false ) to optimize performance and avoid possible deadlocks caused by callers that are unaware of SynchronizationContext used by the library.