C# WhenAll

Summary: in this tutorial, you’ll learn how to use the C# WhenAll() static method of the Task class to create a task that will be completed when all the input tasks are completed.

Introduction to the C# WhenAll() static method

The Task.WhenAll() method creates a task that will be completed once all the input tasks are completed. The method returns a Task object that represents the completion of all the input tasks. The returned task contains the results of all the input tasks.

In practice, the Task.WhenAll() is useful for aggregating results from multiple asynchronous operations.

The following program demonstrates the Task.WhenAll() method:

static async Task<int> DoWork(int taskId)
{
    Console.WriteLine($"Task {taskId} started...");
    await Task.Delay(TimeSpan.FromSeconds(taskId));
    Console.WriteLine($"Task {taskId} finished.");
    return taskId * taskId;
}

var t1 = Task.Run(() => DoWork(1));
var t2 = Task.Run(() => DoWork(2));
var t3 = Task.Run(() => DoWork(3));

int[] results = await Task.WhenAll(t1, t2, t3);

Console.WriteLine("Results:");
Console.WriteLine(string.Join(", ", results));Code language: C# (cs)

Output:

Task 3 started...
Task 2 started...
Task 1 started...
Task 1 finished.
Task 2 finished.
Task 3 finished.
Results:
1, 4, 9Code language: C# (cs)

How it works.

First, define a method called DoWork() that accepts an integer parameter taskId. The method displays a message indicating that the task has started, and delays for a specified number of seconds using the Task.Delay() method, and displays another message showing that the task has finished and returns the square of the taskId:

static async Task<int> DoWork(int taskId)
{
    Console.WriteLine($"Task {taskId} started...");
    await Task.Delay(TimeSpan.FromSeconds(taskId));
    Console.WriteLine($"Task {taskId} finished.");
    return taskId * taskId;
}Code language: C# (cs)

Since the method has an asynchronous operation, it is marked as an async method. Hence, it returns a Task<int> object rather than an integer.

Second, create three tasks using the Task.Run() method that executes the DoWork() method on background threads:

var t1 = Task.Run(() => DoWork(1));
var t2 = Task.Run(() => DoWork(2));
var t3 = Task.Run(() => DoWork(3));Code language: C# (cs)

Third, use the Task.WhenAll() method to wait for three tasks to be completed. The method returns an array of integers stored in the results variable:

int[] results = await Task.WhenAll(t1, t2, t3);Code language: C# (cs)

Finally, show the results to the console:

Console.WriteLine("Results:");
Console.WriteLine(string.Join(", ", results));Code language: C# (cs)

Waiting for multiple HTTP requests to complete using Task.WhenAll() method

The following example shows how to use the Task.WhenAll() and HttpClient to fetch contents asynchronously from multiple URLs and calculate the total bytes:

static async Task<int> Fetch(string url)
{
    Console.WriteLine($"Fetching {url}...");

    var httpClient = new HttpClient();
    var response = await httpClient.GetAsync(url);
    var content = await response.Content.ReadAsStringAsync();

    Console.WriteLine($"Fetched {url} ({content.Length} bytes)");

    return content.Length;
}

var t1 = Task.Run(() => Fetch("https://www.rfc-editor.org/rfc/rfc2616"));
var t2 = Task.Run(() => Fetch("https://www.rfc-editor.org/rfc/rfc2822"));
var t3 = Task.Run(() => Fetch("https://www.rfc-editor.org/rfc/rfc1180"));


var results = await Task.WhenAll(new[] { t1, t2, t3 });

Console.WriteLine($"Total {results.Sum()} bytes");Code language: C# (cs)

Output:

Fetching https://www.rfc-editor.org/rfc/rfc2822...
Fetching https://www.rfc-editor.org/rfc/rfc1180...
Fetching https://www.rfc-editor.org/rfc/rfc2616...
Fetched https://www.rfc-editor.org/rfc/rfc2822 (141338 bytes)
Fetched https://www.rfc-editor.org/rfc/rfc1180 (80606 bytes)
Fetched https://www.rfc-editor.org/rfc/rfc2616 (521792 bytes)
Total 743736 bytesCode language: C# (cs)

How it works.

First, define the Fetch()method that takes a string argument url and returns a Task<int> object:

static async Task<int> Fetch(string url)
{
    Console.WriteLine($"Fetching {url}...");

    var httpClient = new HttpClient();
    var response = await httpClient.GetAsync(url);
    var content = await response.Content.ReadAsStringAsync();

    Console.WriteLine($"Fetched {url} ({content.Length} bytes)");

    return content.Length;
}Code language: C# (cs)

The Fetch() method uses the HttpClient object to fetch the contents of the input url asynchronously. Once it completes downloading the contents of the URL, it gets the size of the contents in bytes. and return it as the integer value wrapped in a Task object.

Second, create three tasks using the Task.Run method to execute the Fetch() method asynchronously:

var t1 = Task.Run(() => Fetch("https://www.rfc-editor.org/rfc/rfc2616"));
var t2 = Task.Run(() => Fetch("https://www.rfc-editor.org/rfc/rfc2822"));
var t3 = Task.Run(() => Fetch("https://www.rfc-editor.org/rfc/rfc1180"));Code language: C# (cs)

Third, wait for all three tasks to be completed using the Task.WhenAll() method and return an array of their results as Task<int[]> object:

var results = await Task.WhenAll(new[] { t1, t2, t3 });Code language: C# (cs)

Finally, calculate the total size of the fetched contents by adding up the size of the content of each task using the Sum() extension method and display the result:

Console.WriteLine($"Total {results.Sum()} bytes");Code language: C# (cs)

Summary

  • Use the Task.WhenAll() method to create a task that will be completed once all the input tasks have finished.
Was this tutorial helpful ?