.NET 4.5 Parallel Extensions – Async

Exclusive offer: get 50% off this eBook here
.NET 4.5 Parallel Extensions Cookbook

.NET 4.5 Parallel Extensions Cookbook — Save 50%

80 recipes to create scalable, task-based parallel programs using .NET 4.5 with this book and ebook

$29.99    $15.00
by Bryan Freeman | August 2013 | Cookbooks

To help ease the complexities of writing parallel code, Microsoft has introduced the Parallel Extensions in .NET framework. The Parallel Extensions or Task Parallel Library helps us to write efficient, scalable, parallel code in a natural manner without having to deal directly with the classic threading model or the thread pool. Using these extensions, we can conveniently write parallel versions of existing sequential code that can automatically utilize all available processors, thus providing significant performance gains.

In this article, by Bryan Freeman, the author of .NET 4.5 Parallel Extensions Cookbook, we will walk through several recipes that show how to maintain a responsive UI or scalable services by using the new Task-based Asynchronous Pattern (TAP) of the .NET Framework 4.5.

This article will cover the following recipes:

  • Creating an async method

  • Handling Exceptions in asynchronous code

  • Cancelling an asynchronous operation

  • Cancelling async operation after timeout period

  • Processing multiple async tasks as they complete

(For more resources related to this topic, see here.)

Creating an async method

The TAP is a new pattern for asynchronous programming in .NET Framework 4.5. It is based on a task, but in this case a task doesn't represent work which will be performed on another thread. In this case, a task is used to represent arbitrary asynchronous operations.

Let's start learning how async and await work by creating a Windows Presentation Foundation (WPF ) application that accesses the web using HttpClient. This kind of network access is ideal for seeing TAP in action. The application will get the contents of a classic book from the web, and will provide a count of the number of words in the book.

How to do it…

Let's go to Visual Studio 2012 and see how to use the async and await keywords to maintain a responsive UI by doing the web communications asynchronously.

  1. Start a new project using the WPF Application project template and assign WordCountAsync as Solution name .
  2. Begin by opening MainWindow.xaml and adding the following XAML to create a simple user interface containing Button and TextBlock:

    <Window x:Class="WordCountAsync.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title="WordCountAsync" Height="350" Width="525"> <Grid> <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="219,195,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <TextBlock x:Name="TextResults" HorizontalAlignment="Left" Margin="60,28,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="139" Width="411"/> </Grid> </Window>

  3. Next, open up MainWindow.xaml.cs. Go to the Project and add a reference to System.Net.Http.
  4. Add the following using directives to the top of your MainWindow class:

    using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Windows;

  5. At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array.

    char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' };

  6. Add a button click event for the StartButton and add the async modifier to the method signature to indicate that this will be a async method. Please note that async methods that return void are normally only used for event handlers, and should be avoided.

    private async void StartButton_Click(object sender, RoutedEventArgs e) { }

  7. Next, let's create a async method called GetWordCountAsync that returns Task<int>. This method will create HttpClient and call its GetStringAsync method to download the book contents as a string. It will then use the Split method to split the string into a wordArray. We can return the count of the wordArray as our return value.

    public async Task<int> GetWordCountAsync() { TextResults.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); var bookContents = await client.GetStringAsync
    (@"http://www.gutenberg.org/files/2009/2009.txt"); var wordArray = bookContents.Split
    (delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); }

  8. Finally, let's complete the implementation of our button click event. The Click event handler will just call GetWordCountAsync with the await keyword and display the results to TextBlock.

    private async void StartButton_Click(object sender, RoutedEventArgs e) { var result = await GetWordCountAsync(); TextResults.Text += String.Format
    ("Origin of Species word count: {0}",result); }

  9. In Visual Studio 2012, press F5 to run the project. Click on the Start button, and your application should appear as shown in the following screenshot:

How it works…

In the TAP, asynchronous methods are marked with an async modifier. The async modifier on a method does not mean that the method will be scheduled to run asynchronously on a worker thread. It means that the method contains control flow that involves waiting for the result of an asynchronous operation, and will be rewritten by the compiler to ensure that the asynchronous operation can resume this method at the right spot.

Let me try to put this a little more simply. When you add the async modifier to a method, it indicates that the method will wait on an asynchronous code to complete. This is done with the await keyword. The compiler actually takes the code that follows the await keyword in an async method and turns it into a continuation that will run after the result of the async operation is available. In the meantime, the method is suspended, and control returns to the method's caller.

If you add the async modifier to a method, and then don't await anything, it won't cause an error. The method will simply run synchronously.

An async method can have one of the three return types: void, Task, or Task<TResult>. As mentioned before, a task in this context doesn't mean that this is something that will execute on a separate thread. In this case, task is just a container for the asynchronous work, and in the case of Task<TResult>, it is a promise that a result value of type TResult will show up after the asynchronous operation completes.

In our application, we use the async keyword to mark the button click event handler as asynchronous, and then we wait for the GetWordCountAsync method to complete by using the wait keyword.

private async void StartButton_Click(object sender, RoutedEventArgs e) { StartButton.Enabled = false; var result = await GetWordCountAsync(); TextResults.Text += String.Format("Origin of Species word count: {0}", .................. result); StartButton.Enabled = true; }

The code that follows the await keyword, in this case, the same line of code that updates TextBlock, is turned by the compiler into a continuation that will run after the integer result is available.

If the Click event is fired again while this asynchronous task is in progress, another asynchronous task is created and awaited. To prevent this, it is a common practice to disable the button that is clicked.

It is a convention to name an asynchronous method with an Async postfix, as we have done with GetWordCountAsync.

Handling Exceptions in asynchronous code

So how would you add Exception handling to code that is executed asynchronously? In previous asynchronous patterns, this was very difficult to achieve. In C# 5.0 it is much more straightforward because you just have to wrap the asynchronous function call with a standard try/catch block.

On the surface this sounds easy, and it is, but there is more going on behind the scene that will be explained right after we build our next example application.

For this recipe, we will return to our classic books word count scenario, and we will be handling an Exception thrown by HttpClient when it tries to get the book contents using an incorrect URL.

How to do it…

Let's build another WPF application and take a look at how to handle Exceptions when something goes wrong in one of our asynchronous methods.

  1. Start a new project using the WPF Application project template and assign AsyncExceptions as Solution name .
  2. Begin by opening MainWindow.xaml and adding the following XAML to create a simple user interface containing Button and a TextBlock:

    <Window x:Class="WordCountAsync.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title="WordCountAsync" Height="350" Width="525"> <Grid> <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="219,195,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <TextBlock x:Name="TextResults" HorizontalAlignment="Left" Margin="60,28,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="139" Width="411"/> </Grid> </Window>

  3. Next, open up MainWindow.xaml.cs. Go to the Project Explorer , right-click on References , click on Framework from the menu on the left side of the Reference Manager , and then add a reference to System.Net.Http.
  4. Add the following using directives to the top of your MainWindow class:

    using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Windows;

  5. At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array.

    char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' };

  6. Now let's create our GetWordCountAsync method. This method will be very similar to the last recipe, but it will be trying to access the book on an incorrect URL. The asynchronous code will be wrapped in a try/catch block to handle Exception. We will also use a finally block to dispose of HttpClient.

    public async Task<int> GetWordCountAsync() { ResultsTextBlock.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); try { var bookContents = await client.GetStringAsync
    (@"http://www.gutenberg.org/files/2009/No_Book_Here.txt"); var wordArray = bookContents.Split
    (delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } catch (Exception ex) { ResultsTextBlock.Text += String.Format("An error has occurred:
    {0} \n", ex.Message); return 0; } finally { client.Dispose(); } }

  7. Finally, let create the Click event handler for our StartButton. This is pretty much the same as the last recipe, just wrapped in a try/catch block. Don't forget to add the async modifier to the method signature.

    private async void StartButton_Click(object sender, RoutedEventArgs e) { try { var result = await GetWordCountAsync(); ResultsTextBlock.Text += String.Format
    ("Origin of Species word count: {0}", result); } catch(Exception ex) { ResultsTextBlock.Text += String.Format("An error has occurred: {0} \n", ex.Message); } }

  8. Now, in Visual Studio 2012, press F5 to run the project. Click on the Start button. Your application should appear as shown in the following screenshot:

How it works…

Wrapping your asynchronous code in a try/catch block is pretty easy. In fact, it hides some of the complex work Visual Studio 2012 to doing for us.

To understand this, you need to think about the context in which your code is running.

When the TAP is used in Windows Forms or WPF applications, there's already a context that the code is running in, such as the message loop UI thread. When async calls are made in those applications, the awaited code goes off to do its work asynchronously and the async method exits back to its caller. In other words, the program execution returns to the message loop UI thread.

The Console applications don't have the concept of a context. When the code hits an awaited call inside the try block, it will exit back to its caller, which in this case is Main. If there is no more code after the awaited call, the application ends without the async method ever finishing.

To alleviate this issue, Microsoft included async compatible context with the TAP that is used for Console apps or unit test apps to prevent this inconsistent behavior. This new context is called GeneralThreadAffineContext.

Do you really need to understand these context issues to handle async Exceptions? No, not really. That's part of the beauty of the Task-based Asynchronous Pattern.

Cancelling an asynchronous operation

In .NET 4.5, asynchronous operations can be cancelled in the same way that parallel tasks can be cancelled, by passing in CancellationToken and calling the Cancel method on CancellationTokenSource.

In this recipe, we are going to create a WPF application that gets the contents of a classic book over the web and performs a word count. This time though we are going to set up a Cancel button that we can use to cancel the async operation if we don't want to wait for it to finish.

How to do it…

Let's create a WPF application to show how we can add cancellation to our asynchronous methods.

  1. Start a new project using the WPF Application project template and assign AsyncCancellation as Solution name .
  2. Begin by opening MainWindow.xaml and adding the following XAML to create our user interface. In this case, the UI contains TextBlock, StartButton, and CancelButton.

    <Window x:Class="AsyncCancellation.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="AsyncCancellation" Height="400" Width="599"> <Grid Width="600" Height="400"> <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="142,183,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <Button x:Name="CancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="379,185,0,0" VerticalAlignment="Top" Width="75" Click="CancelButton_Click"/> <TextBlock x:Name="TextResult" HorizontalAlignment="Left" Margin="27,24,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="135" Width="540"/> </Grid> </Window>

  3. Next, open up MainWindow.xaml.cs, click on the Project Explorer , and add a reference to System.Net.Http.
  4. Add the following using directives to the top of your MainWindow class:

    using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Windows;

  5. At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array.

    char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' };

  6. Next, let's create the GetWordCountAsync method. This method is very similar to the method explained before. It needs to be marked as asynchronous with the async modifier and it returns Task<int>. This time however, the method takes a CancellationToken parameter. We also need to use the GetAsync method of HttpClient instead of the GetStringAsync method, because the former supports cancellation, whereas the latter does not. We will add a small delay in the method so we have time to cancel the operation before the download completes.

    public async Task<int> GetWordCountAsync(CancellationToken ct) { TextResult.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); await Task.Delay(500); try { HttpResponseMessage response = await client.GetAsync
    (@"http://www.gutenberg.org/files/2009/2009.txt", ct); var words = await response.Content.ReadAsStringAsync(); var wordArray = words.Split
    (delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } finally { client.Dispose(); } }

  7. Now, let's create the Click event handler for our CancelButton. This method just needs to check if CancellationTokenSource is null, and if not, it calls the Cancel method.

    private void CancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } }

  8. Ok, let's finish up by adding a Click event handler for StartButton. This method is the same as explained before, except we also have a catch block that specifically handles OperationCancelledException. Don't forget to mark the method with the async modifier.

    public async Task<int> GetWordCountAsync(CancellationToken ct) { TextResult.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); await Task.Delay(500); try { HttpResponseMessage response = await client.GetAsync
    (@"http://www.gutenberg.org/files/2009/2009.txt", ct); var words = await response.Content.ReadAsStringAsync(); var wordArray = words.Split
    (delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } finally { client.Dispose(); } }

  9. In Visual Studio 2012, press F5 to run the project Click on the Start button, then the Cancel button. Your application should appear as shown in the following screenshot:

How it works…

Cancellation is an aspect of user interaction that you need to consider to build a professional async application. In this example, we implemented cancellation by using a Cancel button, which is one of the most common ways to surface cancellation functionality in a GUI application.

In this recipe, cancellation follows a very common flow.

  1. The caller (start button click event handler) creates a CancellationTokenSource object.

    private async void StartButton_Click(object sender, RoutedEventArgs e) { cts = new CancellationTokenSource(); ... }

  2. The caller calls a cancelable method, and passes CancellationToken from CancellationTokenSource (CancellationTokenSource.Token).

    public async Task<int> GetWordCountAsync(CancellationToken ct ) { ... HttpResponseMessage response = await client.GetAsync
    (@"http://www.gutenberg.org/files/2009/2009.txt", ct ); ... }

  3. The cancel button click event handler requests cancellation using the CancellationTokenSource object (CancellationTokenSource.Cancel()).

    private void CancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } }

  4. The task acknowledges the cancellation by throwing OperationCancelledException, which we handle in a catch block in the start button click event handler.
.NET 4.5 Parallel Extensions Cookbook 80 recipes to create scalable, task-based parallel programs using .NET 4.5 with this book and ebook
Published: July 2013
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Cancelling async operation after timeout period

Another common scenario for cancelling asynchronous tasks is to set a timeout period by using the CancellationTokenSource.CancelAfter method. This method schedules the cancellation of any associated tasks that aren't complete within the period of time that's designated by the CancelAfter expression.

In this recipe, we are going to create a WPF application that gets the contents of a classic book over the web and performs a word count. This time though, we are going to set a timeout period after which the task gets cancelled.

How to do it…

Now, let's see how we can create an asynchronous task that cancels after a specified timeout period.

  1. Start a new project using the WPF Application project template and assign CancelAfterTimeout as Solution name .
  2. Begin by opening MainWindow.xaml and add the following XAML to create our user interface:

    <Window x:Class="CancelAfterTimeout.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> <Button x:Name="StartButton" Content="Start Download" HorizontalAlignment="Left" Margin="194,264,0,0" VerticalAlignment="Top" Width="125" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <TextBlock x:Name="TextResult" HorizontalAlignment="Left" Margin="48,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="213" Width="420"/> <Label Content="Timeout (in ms):" HorizontalAlignment="Left" Margin="163,227,0,0" VerticalAlignment="Top"/> <TextBox x:Name="TextTimeout" HorizontalAlignment="Left" Height="23" Margin="277,231,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="50"/> </Grid> </Window>

  3. Next, open up MainWindow.xaml.cs. Go to the Project Explorer and add a reference to System.Net.Http.
  4. Add the following using directives to the top of your MainWindow class:

    using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Windows;

  5. At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array.

    char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' };

  6. Next, let's create the GetWordCountAsync method. This method is exactly the same as the last recipe. It needs to be marked as asynchronous with the async modifier and it returns Task<int>. The method takes a CancellationToken parameter. We will add a small delay in the method so we have time to cancel the operation before the download completes.

    public async Task<int> GetWordCountAsync(CancellationToken ct) { TextResult.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); await Task.Delay(500); try { HttpResponseMessage response = await client.GetAsync
    (@"http://www.gutenberg.org/files/2009/2009.txt", ct); var words = await response.Content.ReadAsStringAsync(); var wordArray = words.Split
    (delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } finally { client.Dispose(); } }

  7. Ok, let's finish up by adding a Click event handler for StartButton. This method is similar to the last recipe, except we call the CancellationTokenSource.CancelAfter method, passing it the value of our timeout textbox. Don't forget to mark the method with the async modifier.

    private async void StartButton_Click(object sender, RoutedEventArgs e) { StartButton.IsEnabled = false; try { tokenSource = new CancellationTokenSource(); var timeoutPeriod = int.Parse(TextTimeout.Text); tokenSource.CancelAfter(timeoutPeriod); await GetWordCount(tokenSource.Token); } catch (OperationCanceledException) { TextResult.Text += "The operation was cancelled. \n"; } catch (Exception) { TextResult.Text +=
    "The operation failed to complete due to an exception. \n"; } finally { StartButton.IsEnabled = true; } }

  8. In Visual Studio 2012, press F5 to run the project. Set the timeout value to 100. Your application should appear as shown in the following screenshot:

How it works…

The application is very similar to the application we created in the last recipe, except this time the Cancel button isn't used. The actual cancellation follows a similar flow however.

  1. The caller (start button click event handler) creates a CancellationTokenSource object, and then calls the CancelAfter method to pass in the timeout value.

    private async void StartButton_Click(object sender, RoutedEventArgs e) { StartButton.IsEnabled = false; try { tokenSource = new CancellationTokenSource(); var timeoutPeriod = int.Parse(TextTimeout.Text); tokenSource.CancelAfter(timeoutPeriod); ... } ... }

  2. The caller calls a cancelable method, and passes CancellationToken from CancellationTokenSource (CancellationTokenSource.Token).

    public async Task<int> GetWordCountAsync(CancellationToken ct) { ... HttpResponseMessage response = await client.GetAsync
    (@"http://www.gutenberg.org/files/2009/2009.txt", ct); ... }

  3. After the timeout period expires, CancellationTokenSource triggers a cancellation same as if we had made a call to CancellationTokenSource.Cancel.

    private void CancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } }

The task acknowledges the cancellation by throwing OperationCancelledException, which we handle in a catch block in the start button click event handler.

Processing multiple async tasks as they complete

Many of the methods of the Task class that we learned about in are as useful when writing an asynchronous code as they are when writing a parallel code. In this recipe, we are going to download the contents of multiple books and use Task.WhenAny to process them as they finish.

This application will use a while loop to create a collection of tasks. Each task downloads the contents of a specified book. In each iteration of a loop, an awaited call to WhenAny returns the task in the collection of tasks that finishes first. That task is removed from the collection and processed. The loop repeats until the collection contains no more tasks.

How to do it…

Now, let's create a WPF application that creates multiple asynchronous tasks and processes them as they complete.

  1. Start a new project using the WPF Application project template and assign AsyncMultipleRequest as Solution name .
  2. Begin by opening MainWindow.xaml and adding the following XAML to create our user interface:

    <Window x:Class="AsyncMultipleRequest.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> <Button x:Name="StartButton" Content="Start Download" HorizontalAlignment="Left" Margin="194,264,0,0" VerticalAlignment="Top" Width="125" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <TextBlock x:Name="TextResult" HorizontalAlignment="Left" Margin="48,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="213" Width="420"/> </Grid> </Window>

  3. Next, open up MainWindow.xaml.cs. Go to the Project Explorer , and add a reference to System.Net.Http.
  4. Add the following using directives to the top of your MainWindow class:

    using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Documents; using System.Net.Http;

  5. At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array.

    char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' };

  6. Let's start by creating a helper function that builds a list of KeyValuePair<string,string> which represents our book titles and URLs.

    private List<KeyValuePair<string, string>> GetBookUrls() { var urlList = new List<KeyValuePair<string, string>> { new KeyValuePair<string,string>("Origin of Species", "http://www.gutenberg.org/files/2009/2009.txt"), new KeyValuePair<string,string>("Beowulf", "http://www.gutenberg.org/files/16328/16328-8.txt"), new KeyValuePair<string,string>("Ulysses", "http://www.gutenberg.org/files/4300/4300.txt") }; return urlList; }

  7. Now let's create an async method that does the book download and returns KeyValuePair<string, int> that represents our book titles and word count. This method will need to accept a KeyValuePair<string, string> parameter representing the book title and URL. The method also needs an HttpClient parameter.

    async Task<KeyValuePair<string,int>>
    ProcessBook(KeyValuePair<string,string> book, HttpClient client) { var bookContents = await client.GetStringAsync(book.Value); var wordArray = bookContents.Split
    (delimiters, StringSplitOptions.RemoveEmptyEntries); return new KeyValuePair<string,int>(book.Key,wordArray.Count()); }

  8. Now we need to create another async method called GetMultipleWordCount. This method executes a query on our list of books. Each query calls the ProcessBook method to actually do the download and obtain the word count. After the query, we set up a while loop that loops while our list of book processing tasks is greater than zero. Each iteration of the loop awaits a call to Task.WhenAny. When a task is completed, the results are written out and Task is removed from the Task list. This method takes no parameters and returns Task.

    public async Task GetMultipleWordCount() { var client = new HttpClient(); var results = new List<KeyValuePair<string, int>>(); var urlList = GetBookUrls(); IEnumerable<Task<KeyValuePair<string,int>>> bookQuery = from book in urlList select ProcessBook(book, client); List<Task<KeyValuePair<string,int>>> bookTasks = bookQuery.ToList(); while (bookTasks.Count > 0) { Task<KeyValuePair<string, int>>
    firstFinished = await Task.WhenAny(bookTasks); bookTasks.Remove(firstFinished); var thisBook = await firstFinished; TextResult.Text += String.Format
    ("Finished downloading {0}. Word count: {1}\n", thisBook.Key, thisBook.Value); } }

  9. Finally, let's create our start button click event handler. The handler only needs to call the GetMultipleWordCount method.

    private async void StartButton_Click(object sender, RoutedEventArgs e) { TextResult.Text += "Started downloading books...\n"; await GetMultipleWordCount(); TextResult.Text += "Finished downloading books...\n"; }

  10. In Visual Studio 2012, press F5 to run the project. Your application should appear as shown in the following screenshot:

How it works…

We have already seen in the previous recipes that the WhenAny method of the Task class can be used on a list of parallel tasks to continue processing when any of the tasks in the array is complete.

Even though a task in the async context doesn't mean that our list of async tasks are running in parallel on separate worker threads, we can still use the WhenAny method to handle async requests as they complete.

In this recipe, we downloaded the text of multiple books and displayed the word count of each of the books as the download finished. The start button's click event handler doesn't do much other than add some text to TextBlock and await a call to the GetMultipleWordCount method. After creating HttpClient, the GetMultipleWordCount method makes a call to the GetBookUrls helper method that we created, which just returns a list of three books and their URLs.

After getting the list of books and their URLs, the GetMultipleWordCount method creates IEnumerable<Task<TResult>> by executing a LINQ query that calls the ProcessBook method on each book in the list.

var bookQuery = from book in urlList select ProcessBook(book, client); var bookTasks = bookQuery.ToList();

Next, we set up a while loop on the condition that bookTasks.Count is greater than zero. In the body of the while loop, we await a call to the Task.WhenAny method, which will return when the first list of tasks is complete. We then remove this Task from bookTasks so the count is decremented. Below that, we await the firstFinished task variable. This has the effect of the compiler creating a continuation for us at this point that will run, as soon as the task variable firstFinished is completed, the compiler-created continuation will contain the code to update the TextBlock with the word count for the book.

var firstFinished = await Task.WhenAny(bookTasks); bookTasks.Remove(firstFinished); var thisBook = await firstFinished; // The compiler will create a continuation at this point that will run // when the task referenced by the firstFinished variable completes. TextResult.Text+= String.Format("Finished downloading {0}. Word count:{1}\n", thisBook.Key, thisBook.Value);

Summary

In this article we discussed about the Async feature of the .NET Framework, and using asynchrony to maintain a responsive UI. We've all seen client applications that don't respond to mouse events or update the display for noticeable periods of time due to synchronous code holding on to the single UI thread for too long.

Resources for Article :


Further resources on this subject:


.NET 4.5 Parallel Extensions Cookbook 80 recipes to create scalable, task-based parallel programs using .NET 4.5 with this book and ebook
Published: July 2013
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

About the Author :


Bryan Freeman

Bryan Freeman is a developer, architect, and writer on Parallel and Distributed Technologies. With over 15 years of experience delivering solutions across industry sectors such as healthcare, finance, and transportation, he specializes in technologies such as Microsoft .NET, Windows Communication Foundation, Windows Workflow, and Windows Azure to help clients deliver business value and drive revenue while reducing operational costs.

As an active speaker, writer, and blogger, Bryan is a passionate community advocate for those who develop solutions based on .NET technologies. He is also an active member of the Scottsdale Institute and the Veterans of Foreign Wars.

Books From Packt


 .Net Framework 4.5 Expert Programming Cookbook
.Net Framework 4.5 Expert Programming Cookbook

 .NET Compact Framework 3.5 Data Driven Applications
.NET Compact Framework 3.5 Data Driven Applications

 .NET 4.0 Generics Beginner’s Guide
.NET 4.0 Generics Beginner’s Guide

 Microsoft .NET Framework 4.5 Quickstart Cookbook
Microsoft .NET Framework 4.5 Quickstart Cookbook

Visual Studio 2012 and .NET 4.5 Expert Development Cookbook
Visual Studio 2012 and .NET 4.5 Expert Development Cookbook

 Instant Lucene.NET [Instant]
Instant Lucene.NET [Instant]

 Instant Ext.NET Application Development [Instant]
Instant Ext.NET Application Development [Instant]

Ext.NET Web Application Development
Ext.NET Web Application Development


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software