Syntax
await Task.WhenAll(task1, task2, task3);Examples
Running Tasks Sequentially (Slower)
Awaiting each task one at a time adds up their durations.
async Task<int> DownloadAsync(string name, int delayMs)
{
await Task.Delay(delayMs);
Console.WriteLine($"{name} done");
return delayMs;
}
// Sequential: total time is roughly the SUM of all delays
int a = await DownloadAsync("File A", 300);
int b = await DownloadAsync("File B", 300);
int c = await DownloadAsync("File C", 300);
// Takes about 900ms totalTask.WhenAll: Running Concurrently
Starting all tasks first, then awaiting them together - total time is roughly the longest single task.
async Task<int> DownloadAsync(string name, int delayMs)
{
await Task.Delay(delayMs);
Console.WriteLine($"{name} done");
return delayMs;
}
Task<int> taskA = DownloadAsync("File A", 300);
Task<int> taskB = DownloadAsync("File B", 300);
Task<int> taskC = DownloadAsync("File C", 300);
int[] results = await Task.WhenAll(taskA, taskB, taskC);
Console.WriteLine($"Total: {results.Sum()}ms of work, but ran concurrently");
// Takes about 300ms total, not 900msTask.WhenAny: First to Finish
Completing as soon as any one of several tasks finishes - useful for timeouts or racing multiple sources.
async Task<string> FetchFromServerAsync(string server, int delayMs)
{
await Task.Delay(delayMs);
return $"Response from {server}";
}
Task<string> primary = FetchFromServerAsync("Primary", 500);
Task<string> backup = FetchFromServerAsync("Backup", 200);
Task<string> firstDone = await Task.WhenAny(primary, backup);
Console.WriteLine(await firstDone); // Response from Backup (it was faster)Best practices
- Use Task.WhenAll() whenever you have several independent async operations that do not depend on each other, for a significant speed-up over awaiting them sequentially
- Start all tasks first (without awaiting each one individually), then pass them all to Task.WhenAll() - awaiting immediately after starting each one defeats the concurrency benefit
- Use Task.WhenAny() for timeout patterns, by racing your real operation against a Task.Delay() representing the timeout
- Remember Task.WhenAll() propagates exceptions from all failed tasks (via an AggregateException when awaited via .Result, or the first exception when awaited directly) - handle this appropriately
At a glance
- Purpose
- Applications on the .NET platform
- File extension
- .cs
- Runs in
- .NET runtime
- Usually used with
- .NET SDK and libraries
Specifications & further reading
Related C# documentation
async and await
The async and await keywords let you write asynchronous code that reads almost like ordinary sequential code. Marking a method async allows it to use await, which pauses execution at that point until the awaited operation completes, without blocking the calling thread - freeing it up to do other work in the meantime. This is essential for keeping applications responsive during I/O-bound work like network calls, file access, or database queries.Task & Task<T>
Task represents an asynchronous operation that may still be running, and Task<T> represents one that will eventually produce a value of type T. Tasks are the building block that async/await is built on top of - an async method actually returns a Task (or Task<T>) that the caller can await, check the status of, or attach continuations to.CancellationToken
CancellationToken provides a standard, cooperative way to signal that an asynchronous operation should stop before it completes naturally. A CancellationTokenSource creates the token and controls when cancellation is requested; the token itself is passed into async methods, which check it periodically (or pass it to another cancellable operation) and can throw an OperationCanceledException to unwind cleanly.Async Pitfalls: void vs Task, ConfigureAwait
A few recurring mistakes trip up many C# developers new to async: using async void instead of async Task (which makes exceptions impossible to catch normally), and blocking on async code with .Result or .Wait() (which can cause a deadlock in contexts with a synchronization context, like older ASP.NET or WPF apps). Understanding these pitfalls up front avoids some of the most common async-related bugs.
The async and await keywords let you write asynchronous code that reads almost like ordinary sequential code. Marking a method async allows it to use await, which pauses execution at that point until the awaited operation completes, without blocking the calling thread - freeing it up to do other work in the meantime. This is essential for keeping applications responsive during I/O-bound work like network calls, file access, or database queries.Task & Task<T>
Task represents an asynchronous operation that may still be running, and Task<T> represents one that will eventually produce a value of type T. Tasks are the building block that async/await is built on top of - an async method actually returns a Task (or Task<T>) that the caller can await, check the status of, or attach continuations to.CancellationToken
CancellationToken provides a standard, cooperative way to signal that an asynchronous operation should stop before it completes naturally. A CancellationTokenSource creates the token and controls when cancellation is requested; the token itself is passed into async methods, which check it periodically (or pass it to another cancellable operation) and can throw an OperationCanceledException to unwind cleanly.Async Pitfalls: void vs Task, ConfigureAwait
A few recurring mistakes trip up many C# developers new to async: using async void instead of async Task (which makes exceptions impossible to catch normally), and blocking on async code with .Result or .Wait() (which can cause a deadlock in contexts with a synchronization context, like older ASP.NET or WPF apps). Understanding these pitfalls up front avoids some of the most common async-related bugs.