C# > Asynchronous Programming > Tasks and async/await > WhenAll and WhenAny
Using Task.WhenAll to Process Multiple Web Requests Concurrently
This snippet demonstrates how to use Task.WhenAll to execute multiple asynchronous operations concurrently and wait for all of them to complete before proceeding. It simulates making several web requests and processing their responses.
Code Example
This C# code demonstrates the usage of Task.WhenAll for parallel execution of asynchronous web requests. The Main method creates a list of URLs and uses FetchUrlContentAsync to asynchronously fetch the content of each URL. Task.WhenAll is then used to wait for all the fetch operations to complete, handling potential exceptions that might occur during the process. The results are then processed and displayed.  Error handling is included to demonstrate how to gracefully handle exceptions when some of the tasks fail.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
public class WhenAllExample
{
    public static async Task Main(string[] args)
    {
        // Define a list of URLs to fetch
        List<string> urls = new List<string>()
        {
            "https://www.example.com",
            "https://www.microsoft.com",
            "https://www.google.com"
        };
        Console.WriteLine("Starting multiple web requests concurrently...");
        // Create a list of tasks, each fetching a URL
        List<Task<string>> tasks = new List<Task<string>>();
        foreach (string url in urls)
        {
            tasks.Add(FetchUrlContentAsync(url));
        }
        // Use Task.WhenAll to wait for all tasks to complete
        try
        {
            string[] contents = await Task.WhenAll(tasks);
            Console.WriteLine("All web requests completed.\n");
            // Process the content of each URL
            for (int i = 0; i < urls.Count; i++)
            {
                Console.WriteLine($"Content from {urls[i]}:\n{contents[i].Substring(0, Math.Min(contents[i].Length, 100))}...\n"); // Display first 100 characters
            }
        }
        catch (AggregateException ex)
        {
            Console.WriteLine("One or more web requests failed:");
            foreach (var innerException in ex.InnerExceptions)
            {
                Console.WriteLine($"  - {innerException.Message}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An unexpected error occurred: {ex.Message}");
        }
        Console.WriteLine("Program finished.");
    }
    // Asynchronous method to fetch the content of a URL
    static async Task<string> FetchUrlContentAsync(string url)
    {
        using (HttpClient client = new HttpClient())
        {
            Console.WriteLine($"Fetching content from {url}...");
            try
            {
                HttpResponseMessage response = await client.GetAsync(url);
                response.EnsureSuccessStatusCode(); // Throw exception for bad status codes
                return await response.Content.ReadAsStringAsync();
            }
            catch (HttpRequestException ex)
            {
                Console.WriteLine($"Error fetching {url}: {ex.Message}");
                return "Error: Could not fetch content.";
            }
        }
    }
}Concepts Behind the Snippet
Task.WhenAll takes an array (or IEnumerable) of Task objects and returns a single Task that represents the completion of all the tasks in the array. It's useful when you need to perform multiple independent operations concurrently and want to wait for all of them to finish before proceeding. It returns a Task that completes when all of its constituent tasks complete.  If any of the input tasks faults, the returned task will also fault, aggregating all exceptions into an AggregateException. If a cancellation token is provided, cancellation of the input tasks will also cause the returned task to complete with a faulted state.
Real-Life Use Case
Imagine a scenario where you need to fetch data from multiple external APIs to construct a dashboard or report. Using Task.WhenAll allows you to initiate all the API requests concurrently, significantly reducing the overall time required to gather the necessary data compared to making sequential requests.  Another example is processing multiple files from a directory. Each file processing operation can be represented as a Task, and Task.WhenAll can be used to wait for all file processing operations to complete before moving on to the next stage.
Best Practices
Task.WhenAll in a try-catch block to handle potential exceptions.  Pay attention to the AggregateException that might contain multiple exceptions from individual tasks.Task.WhenAll are already started. If you create tasks but don't start them (e.g., using new Task(...) without calling Start()), Task.WhenAll will wait indefinitely.  Use Task.Run or async methods to create and start tasks efficiently.async/await. Consider using ConfigureAwait(false) to avoid deadlocks, especially in library code.
Interview Tip
Be prepared to discuss the advantages and disadvantages of using Task.WhenAll compared to other concurrency mechanisms like parallel loops.  Explain how it improves performance by allowing tasks to run concurrently and how it simplifies error handling by aggregating exceptions from all tasks. Also, understand the difference between Task.WhenAll and Task.WhenAny.
When to Use Task.WhenAll
                        
                        
                            Use Task.WhenAll when you have multiple independent asynchronous operations that need to be executed concurrently, and you cannot proceed until all of them have completed successfully (or you have handled their failures).  It's particularly suitable for I/O-bound operations, such as network requests or file access, where the CPU is not heavily utilized.
Memory Footprint
The memory footprint of Task.WhenAll depends on the number of tasks it is waiting for and the size of the data being processed by those tasks.  Each task consumes memory for its state and any captured variables. The aggregated result (e.g., the array of results from the tasks) also consumes memory.  Consider using streams and iterators if dealing with very large datasets to minimize memory usage.
Alternatives
Parallel.ForEach or Parallel.For may be a better choice, as they automatically manage thread pool resources.
Pros
AggregateException, making it easier to handle failures.
Cons
ConfigureAwait(false) in library code.
FAQ
- 
                        What happens if one of the tasks passed toTask.WhenAllthrows an exception?
 If any of the tasks passed toTask.WhenAllthrows an exception, the resulting task will also fault with anAggregateExceptioncontaining the exception(s) thrown by the individual tasks. You need to handle this exception in a try-catch block.
- 
                        How isTask.WhenAlldifferent fromTask.WaitAll?
 Task.WhenAllis an asynchronous method that returns aTask, allowing the calling thread to remain responsive while the tasks are executing.Task.WaitAll, on the other hand, is a synchronous method that blocks the calling thread until all tasks are completed.Task.WhenAllis generally preferred in asynchronous code.
