Async Programming
Concurrency
Task Management
.NET
Performance Optimization

WaitAll vs WaitAny

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

WaitAll and WaitAny solve different coordination problems. WaitAll blocks until every task completes, while WaitAny blocks until at least one task completes. The right choice depends on whether your next step needs the whole batch or can proceed with the first available result.

WaitAll Means Full Barrier Synchronization

Use Task.WaitAll when the program cannot continue until every task in a set is done.

csharp
1using System;
2using System.Threading.Tasks;
3
4class Program
5{
6    static void Main()
7    {
8        Task t1 = Task.Run(() => Task.Delay(300).Wait());
9        Task t2 = Task.Run(() => Task.Delay(500).Wait());
10
11        Task.WaitAll(t1, t2);
12        Console.WriteLine("Both tasks completed.");
13    }
14}

This acts like a barrier. The current thread is blocked until both tasks finish or until an exception or timeout path interrupts the wait.

WaitAny Means First Completion Wins

Use Task.WaitAny when any one completed task is enough to make progress.

csharp
1using System;
2using System.Threading.Tasks;
3
4class Program
5{
6    static void Main()
7    {
8        Task<int> fast = Task.Run(async () => { await Task.Delay(200); return 1; });
9        Task<int> slow = Task.Run(async () => { await Task.Delay(1000); return 2; });
10
11        int index = Task.WaitAny(fast, slow);
12        Console.WriteLine($"Task {index} completed first.");
13    }
14}

The return value is the index of the completed task in the array you passed. The other tasks may still be running afterward.

Exception Behavior Is Different in Practice

With WaitAll, exceptions from faulted tasks are aggregated when the wait completes.

csharp
1try
2{
3    Task.WaitAll(
4        Task.Run(() => throw new InvalidOperationException("bad")),
5        Task.Run(() => Task.Delay(100).Wait())
6    );
7}
8catch (AggregateException ex)
9{
10    Console.WriteLine(ex.InnerExceptions.Count);
11}

With WaitAny, the method only tells you that one task completed. That task may have completed successfully, faulted, or been canceled. You still need to inspect or await the completed task to observe its outcome.

Blocking Waits Are Often the Wrong Tool in Async Code

WaitAll and WaitAny are synchronous waiting APIs. They block the calling thread. In modern async .NET code, Task.WhenAll and Task.WhenAny are often better because they compose with await instead of blocking threads.

csharp
1using System;
2using System.Threading.Tasks;
3
4class Program
5{
6    static async Task Main()
7    {
8        Task<int> a = Task.Run(async () => { await Task.Delay(200); return 10; });
9        Task<int> b = Task.Run(async () => { await Task.Delay(400); return 20; });
10
11        int[] results = await Task.WhenAll(a, b);
12        Console.WriteLine(string.Join(", ", results));
13    }
14}

If you are already inside async application code, prefer the When... versions unless you specifically need blocking behavior.

Typical Use Cases

WaitAll fits workflows like:

  • finishing several independent computations before building one final response
  • waiting for a batch of file writes to complete
  • synchronizing at the end of a parallel stage

WaitAny fits workflows like:

  • racing redundant providers and taking the first healthy response
  • consuming whichever worker finishes first
  • reacting to the earliest signal in a monitoring loop

The right choice follows directly from the business rule.

Cancellation and Cleanup Still Matter

After WaitAny, the losing tasks keep running unless you cancel them. If you launch several redundant operations and only need one, add a CancellationToken strategy so the unused work does not continue burning resources.

With WaitAll, a single failure may still leave other tasks running until they finish or observe cancellation. Design the task bodies with cooperative cancellation if fast shutdown matters.

Common Pitfalls

A common mistake is using WaitAll inside UI threads or request threads, where blocking can freeze the app or reduce throughput. Another is assuming WaitAny cancels the remaining tasks automatically. It does not.

People also often ignore exception handling after WaitAny. The first completed task might be the first failure, not the first success.

Finally, in modern async code, blocking waits are often a sign that await Task.WhenAll or await Task.WhenAny would be the cleaner design.

Summary

  • 'WaitAll blocks until every task completes.'
  • 'WaitAny blocks until one task completes and returns its index.'
  • The two methods solve different coordination problems, not the same problem with different performance.
  • In async .NET code, Task.WhenAll and Task.WhenAny are usually better than blocking waits.
  • Always plan for exceptions, cancellation, and the fate of unfinished tasks.

Course illustration
Course illustration

All Rights Reserved.