What is the difference between Task.WhenAll() and Task.WaitAll() in C#?

The Task.WaitAll() and Task.WhenAll() methods in C# serve different purposes when working with multiple asynchronous tasks. Task.WaitAll() blocks the current thread until all tasks complete, while Task.WhenAll() returns a new task that represents the completion of all provided tasks without blocking the calling thread.

Understanding the difference between these methods is crucial for building responsive applications, especially those with user interfaces where blocking the main thread can freeze the UI.

Syntax

Following is the syntax for Task.WaitAll()

Task.WaitAll(task1, task2, task3);
// or with timeout
Task.WaitAll(new Task[] { task1, task2 }, TimeSpan.FromSeconds(30));

Following is the syntax for Task.WhenAll()

Task combinedTask = Task.WhenAll(task1, task2, task3);
// or with await
await Task.WhenAll(task1, task2, task3);

Key Differences

Task.WaitAll() Task.WhenAll()
Blocks the calling thread Returns a Task without blocking
Synchronous operation Asynchronous operation
Returns void Returns Task or Task<T[]>
Can cause UI freezing Keeps UI responsive

Using Task.WaitAll() - Blocking Approach

The Task.WaitAll() method blocks the current thread and waits for all tasks to complete before proceeding −

using System;
using System.Threading.Tasks;

namespace DemoApplication {
    public class Program {
        static void Main(string[] args) {
            Task task1 = Task.Run(() => {
                for (int i = 0; i < 3; i++) {
                    Console.WriteLine($"Task 1 - iteration {i}");
                    Task.Delay(1000).Wait();
                }
                Console.WriteLine("Task 1 complete");
            });
            
            Task task2 = Task.Run(() => {
                Task.Delay(500).Wait();
                Console.WriteLine("Task 2 complete");
            });
            
            Console.WriteLine("Waiting for tasks to complete...");
            Task.WaitAll(task1, task2);
            Console.WriteLine("All tasks completed!");
        }
    }
}

The output of the above code is −

Waiting for tasks to complete...
Task 1 - iteration 0
Task 2 complete
Task 1 - iteration 1
Task 1 - iteration 2
Task 1 complete
All tasks completed!

Using Task.WhenAll() - Non-Blocking Approach

The Task.WhenAll() method returns a task that completes when all provided tasks complete, without blocking the calling thread −

using System;
using System.Threading.Tasks;

namespace DemoApplication {
    public class Program {
        static async Task Main(string[] args) {
            Task task1 = Task.Run(async () => {
                for (int i = 0; i < 3; i++) {
                    Console.WriteLine($"Task 1 - iteration {i}");
                    await Task.Delay(1000);
                }
                Console.WriteLine("Task 1 complete");
            });
            
            Task task2 = Task.Run(async () => {
                await Task.Delay(500);
                Console.WriteLine("Task 2 complete");
            });
            
            Console.WriteLine("Waiting for tasks to complete...");
            await Task.WhenAll(task1, task2);
            Console.WriteLine("All tasks completed!");
        }
    }
}

The output of the above code is −

Waiting for tasks to complete...
Task 2 complete
Task 1 - iteration 0
Task 1 - iteration 1
Task 1 - iteration 2
Task 1 complete
All tasks completed!

Working with Return Values

When tasks return values, Task.WhenAll() can aggregate the results into an array −

using System;
using System.Threading.Tasks;

namespace DemoApplication {
    public class Program {
        static async Task Main(string[] args) {
            Task<int> task1 = Task.Run(async () => {
                await Task.Delay(1000);
                return 10;
            });
            
            Task<int> task2 = Task.Run(async () => {
                await Task.Delay(500);
                return 20;
            });
            
            Task<int> task3 = Task.Run(() => 30);
            
            int[] results = await Task.WhenAll(task1, task2, task3);
            
            Console.WriteLine("Results:");
            for (int i = 0; i < results.Length; i++) {
                Console.WriteLine($"Task {i + 1} result: {results[i]}");
            }
            
            Console.WriteLine($"Sum: {results[0] + results[1] + results[2]}");
        }
    }
}

The output of the above code is −

Results:
Task 1 result: 10
Task 2 result: 20
Task 3 result: 30
Sum: 60

When to Use Each Method

Use Task.WaitAll() when you need to block the current thread and wait synchronously, typically in console applications or non-UI scenarios where blocking is acceptable.

Use Task.WhenAll() in asynchronous methods with await, especially in UI applications, web applications, or any scenario where you want to maintain responsiveness while waiting for multiple tasks to complete.

Conclusion

Task.WaitAll() blocks the calling thread synchronously, while Task.WhenAll() returns an awaitable task without blocking. Use Task.WhenAll() with await in asynchronous methods to maintain application responsiveness, especially in UI applications where thread blocking can freeze the interface.

Updated on: 2026-03-17T07:04:36+05:30

8K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements