Concurrency
Asynchronous Programming
Task Parallel Library
C#
Multi-threading

WhenAll on the large number of Task

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Task.WhenAll is a coordination primitive, not a throttling mechanism. It waits for a group of tasks to finish, but if you create too many tasks at once, the real problem is usually the amount of work you started before WhenAll ever got involved.

What Task.WhenAll Actually Does

Task.WhenAll takes many tasks and returns one task that completes when all of them complete. It does not create threads on its own, and it does not automatically limit concurrency.

csharp
1using System;
2using System.Linq;
3using System.Threading.Tasks;
4
5var tasks = Enumerable.Range(1, 5)
6    .Select(async i =>
7    {
8        await Task.Delay(100);
9        return i * 2;
10    });
11
12var results = await Task.WhenAll(tasks);
13Console.WriteLine(string.Join(", ", results));

This is a clean pattern when the number of tasks is modest and the operations are naturally asynchronous.

Why Large Numbers of Tasks Can Hurt

If you create thousands or hundreds of thousands of tasks immediately, you may run into:

  • memory pressure from task objects and captured state
  • network or database overload from too many concurrent requests
  • thread-pool pressure if tasks block instead of awaiting
  • long exception lists if many tasks fail together

The problem is not that WhenAll is bad. The problem is that unbounded fan-out can overwhelm the system you are calling or the process that is doing the work.

Limit Concurrency With SemaphoreSlim

When you need to process many items but only allow a limited number to run at once, use a gate:

csharp
1using System;
2using System.Collections.Generic;
3using System.Threading;
4using System.Threading.Tasks;
5
6var gate = new SemaphoreSlim(10);
7var tasks = new List<Task<int>>();
8
9for (int i = 0; i < 100; i++)
10{
11    int value = i;
12    tasks.Add(RunAsync(value, gate));
13}
14
15var results = await Task.WhenAll(tasks);
16
17static async Task<int> RunAsync(int value, SemaphoreSlim gate)
18{
19    await gate.WaitAsync();
20    try
21    {
22        await Task.Delay(50);
23        return value * value;
24    }
25    finally
26    {
27        gate.Release();
28    }
29}

This still uses WhenAll, but only ten tasks are allowed into the critical section at a time.

Batch When Full Fan-Out Is Unnecessary

Sometimes the simplest solution is batching. Process a chunk, await it, then move to the next chunk.

That approach can be easier to reason about when:

  • the work is homogeneous
  • the remote system has strict limits
  • you need predictable memory usage

Batching is not as elegant as fully dynamic throttling, but it is often enough.

Exception Behavior Matters

If multiple tasks fail, the task returned by WhenAll faults after everything completes. You should expect aggregated failures, not just the first exception.

That means production code often logs individual failures or wraps each task so one bad item does not hide the rest of the batch behavior.

Common Pitfalls

One common mistake is assuming Task.WhenAll itself causes excessive parallelism. In reality, the excessive parallelism usually comes from creating too many active tasks before awaiting them.

Another issue is using Task.Run around naturally asynchronous I/O work. That can waste threads without improving throughput.

It is also easy to forget that "asynchronous" does not mean "free." Ten thousand simultaneous HTTP requests can still overwhelm your service dependencies.

Summary

  • 'Task.WhenAll waits for many tasks; it does not automatically throttle them.'
  • Large numbers of tasks can create memory, thread-pool, and downstream-service pressure.
  • Use SemaphoreSlim or batching when you need bounded concurrency.
  • Expect aggregated failures when multiple tasks fault.
  • The real design question is how much work to start at once, not whether WhenAll itself is allowed.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.