Continue "WhenAny" and "WhenAll"

In this recipe we will move from continuing single tasks to setting up continuations for groups of tasks. The two methods we will be looking at are WhenAny and WhenAll. Both methods are static members of the Task.Factory class, and take an array of tasks and Action<Task> as their parameters.

First we will look at the WhenAny continuations. The basic idea here is that we have a group of tasks and we only want to wait for the first and fastest of the group to complete its work before moving on. In our case, we will be downloading the text of three different books, and performing a word count on each. When the first task completes we will display the word count of the winner to the user.

After that we will change to WhenAll and display the results of all three word counts to the user.

How to do it…

Let's build a solution that shows how to conditionally continue a task. The steps are as follows:

  1. Start a new project using the C# Console Application project template and assign Continuation3 as the Solution name.
  2. Add the following using directives to the top of your program class:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;
  3. First, in the Main method of your program class, let's create a character array of delimiters we can use to split our words with, a string constant for the user agent header of our web client, and a Dictionary<string, string> method to hold our book titles and URLs. The dictionary will serve as the state object parameter for our tasks, which will be created in a foreach loop.
    char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', 'u000A' };
    const string headerText = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)";
    var dictionary = new Dictionary<string, string>
    {
      {"Origin of Species", "http://www.gutenberg.org/files/2009/2009.txt"},
        {"Beowulf", "http://www.gutenberg.org/files/16328/16328-8.txt"},
        {"Ulysses", "http://www.gutenberg.org/files/4300/4300.txt"}
    };
  4. Next, let's create a try/catch block with some basic error handling.
    try
    {
      // Loop to create and Continuation will go here
    }
    catch (AggregateException aEx)
    {
      foreach (Exception ex in aEx.InnerExceptions)
      {
        Console.WriteLine("An exception has occured: {0}" + ex.Message);
      }
    }
  5. Inside the try block, let's create a new list of Task<KeyValuePair<string, string>>. Of course, this will be the list of our tasks. Each task will take a KeyValuePair from the dictionary we created in step 3 as their state parameters.
    var tasks = new List<Task<KeyValuePair<string, int>>>();
  6. Now let's create our task in a foreach loop. Each task will read the text of a book from a string, split the string into a character array, and do a word count. Our antecedent tasks return a KeyValuePair<string, int> with the book title and the word count for each book.
    foreach (var pair in dictionary)
    {
      tasks.Add(Task.Factory.StartNew(stateObj =>
      {
        var taskData = (KeyValuePair<string, string>)stateObj;
        Console.WriteLine("Starting task for {0}", taskData.Key);
        var client = new WebClient();
        client.Headers.Add("user-agent", headerText);
        var words = client.DownloadString(taskData.Value);
        var wordArray = words.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
        return new KeyValuePair<string, int>(taskData.Key, wordArray.Count());
      }, pair));
    }
  7. Now let's create the continuation by calling the Task.Factory.WhenAny method. The continuations will just display the title and word count of the winner to the user.
    Task.Factory.ContinueWhenAny(tasks.ToArray(), antecedent =>
    {
        Console.WriteLine("And the winner is: {0}", antecedent.Result.Key);
        Console.WriteLine("Word count: {0}", antecedent.Result.Value);
    }).Wait();
  8. Lastly, after the catch block, prompt the user to exit and wait for the input.
    Console.WriteLine("Complete. Press <Enter> to exit.");
    Console.ReadLine();
  9. In Visual Studio 2012, press F5 to run the project. You should see output similar to the following. Your winner may vary.
    How to do it…
  10. Before moving on, let's change our code a bit and continue when all of our tasks complete. All we need to do is change our method call from Task.Factory.WhenAny to Task.Factory.WhenAll, change the name of the continuation parameter from antecedent to antecedents to reflect plurality, and create a foreach loop in the body of the continuation to loop through the results.
    Task.Factory.ContinueWhenAll(tasks.ToArray(), antecedents =>
    {
        foreach (var antecedent in antecedents)
        {
            Console.WriteLine("Book Title: {0}", antecedent.Result.Key);
            Console.WriteLine("Word count: {0}", antecedent.Result.Value);
        }
    }).Wait();
  11. In Visual Studio 2012, press F5 to run the project. You should see output similar to the following screenshot:
    How to do it…

How it works…

The continuations in this recipe are created a bit differently from the continuations that we have created in previous tasks. Instead of calling the instance method ContinueWith on a Task variable, we are calling the ContinueWhenAny and ContinueWhenAll static methods on Task.FactoryClass.

Task.Factory.ContinueWhenAll(tasks.ToArray(), antecedents =>
{
});

The ContinueWhenAny and ContinueWhenAll methods have a different parameter lists than Task.ContinueWith.

ContinueWhenAny takes an array of Task as its first parameter and a single Action<Task> delegate as its second parameter.

ContinueWhenAny(Task[], Action<Task>)

ContinueWhenAll takes the same array of Task as its first parameter and Action<Task[]> as its second parameter.

ContinueWhenAll(Task[], Action<Task[]>)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.196.171