Skip to content

C# Task.WhenAll — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about C# Task.WhenAll. We cover key concepts, practical examples, and best practices.

You need to download three files, query two databases, or call four APIs. You await each one sequentially, adding the latencies together. Task.WhenAll runs them concurrently, completing when all tasks finish — in roughly the time of the slowest operation.

Wrong

var result1 = await FetchDataAsync(1);
var result2 = await FetchDataAsync(2);
var result3 = await FetchDataAsync(3);
// Total time: sum of all three operations

Output: Works. Takes 3 seconds if each operation is 1 second.

var task1 = FetchDataAsync(1);
var task2 = FetchDataAsync(2);
var task3 = FetchDataAsync(3);
var allResults = await Task.WhenAll(task1, task2, task3);
// Total time: ~1 second (the slowest operation)

Output: Same results. Takes ~1 second instead of 3. The tasks run in parallel.

Task.WhenAll also works with collections and handles faulted tasks:

var tasks = urls.Select(url => httpClient.GetStringAsync(url));
var pages = await Task.WhenAll(tasks);
// If any task faults, the aggregate exception contains all errors

Prevention

  • Use Task.WhenAll for independent operations (no shared state).
  • Use Task.WhenAll with collections by projecting with LINQ.
  • Handle exceptions — wrap in try-catch to collect all errors.
  • Use Task.WhenAny when you need the first completed task.
  • Do not use Task.WhenAll for CPU-bound work — use Parallel.ForEach or Parallel LINQ instead.
  • Use SemaphoreSlim for throttling when you have too many concurrent tasks.

Common Mistakes with task when all

  1. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
  2. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  3. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks

These mistakes appear frequently in real-world CSHARP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

Does Task.WhenAll run tasks on separate threads?

No. Task.WhenAll returns a task that completes when all the input tasks complete. The concurrency comes from the tasks themselves — if you create tasks that perform async I/O, they run concurrently on the thread pool. CPU-bound work needs Task.Run or Parallel APIs.

What happens when a task faults in Task.WhenAll?

The returned task faults with an AggregateException containing all exceptions from all faulted tasks. Unlike await which throws only the first exception, WhenAll preserves all exceptions. Catch AggregateException and inspect .InnerExceptions.

Is there a limit on how many tasks I can pass?

No hard limit, but practical limits apply. Each task uses system resources. For many tasks (1000+), use foreach with SemaphoreSlim to limit concurrency. The WhenAll overload that takes IEnumerable<Task> processes any number of tasks.

Task.WhenAll is used in Doda Browser to fetch extension metadata in parallel. For more C# async, visit DodaTech.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro