Efficiently Managing Multiple Tasks with Task.WhenAll

sharmila subbiah
C# Programming
Published in
3 min readApr 26, 2024
AI Generated pic

Task.WhenAll is used when you have multiple tasks that can run in parallel, and you need to wait for all of them to complete. This method simplifies the management of multiple tasks by returning a single Task that completes when all of the constituent tasks have completed. This single Task can be awaited, allowing the rest of your code to execute only after all the tasks have finished.

The basic syntax of Task.WhenAll is as follows:

Task.WhenAll(IEnumerable<Task> tasks);

It also has an overload that allows for tasks that return a result:

Task.WhenAll(IEnumerable<Task<TResult>> tasks);

How It Works

When Task.WhenAll is called with a collection of tasks, it monitors the state of these tasks. The method returns a new Task that will complete when:

  • All the supplied tasks have completed successfully.
  • One or more tasks have faulted (in which case, the returned task will end in a Faulted state).
  • Any task is canceled (leading the returned task to end in a Canceled state).

Simple Example :

await GetAllDataAsync();

async Task<string> GetWeatherAsync()
{
// Simulating a delay to mimic network latency
await Task.Delay(1000);
return "Weather data: Sunny, 23°C";
}

async Task<string> GetStockDataAsync()
{
// Simulating a delay
await Task.Delay(1500);
return "Stock data: XYZ Corp at $300";
}

async Task<string> GetNewsAsync()
{
// Simulating a delay
await Task.Delay(1200);
return "News data: World leaders meet for summit";
}

async Task GetAllDataAsync()
{
// Simulate three separate data fetch tasks
var fetchWeather = GetWeatherAsync();
var fetchStock = GetStockDataAsync();
var fetchNews = GetNewsAsync();

// Wait for all tasks to complete
var results = await Task.WhenAll(fetchWeather, fetchStock, fetchNews);

// Output results
foreach (var result in results)
{
Console.WriteLine(result);
}
}

Combining with Task.WhenAny:

Sometimes, we might want to take action as soon as any task completes but still continue waiting for the others. This can be done by combining Task.WhenAll with Task.WhenAny:

async Task GetAllDataAsync()
{
// Simulate three separate data fetch tasks
var fetchWeather = GetWeatherAsync();
var fetchStock = GetStockDataAsync();
var fetchNews = GetNewsAsync();

// Wait for all tasks to complete
var allTasks = new[] { fetchWeather, fetchStock, fetchNews };

var whenAllTask = Task.WhenAll(allTasks);
var whenAnyTask = Task.WhenAny(allTasks);

// Process the first completed task
var firstCompletedTask = await whenAnyTask;
Console.WriteLine($"First completed result: {await firstCompletedTask}");

// Continue processing other tasks
await whenAllTask; // Ensures all tasks are completed
Console.WriteLine("All tasks completed.");
}

Why It’s Useful:

Task.WhenAny: We use Task.WhenAny to process the first completing task. This is beneficial for taking immediate action, such as updating the user interface with preliminary data, thus enhancing the application's responsiveness.

Task.WhenAll: Despite handling the first task immediately, it’s crucial to ensure that all tasks complete by awaiting Task.WhenAll. This ensures that no operation is left behind, maintaining all necessary clean-up and final processing steps.

Conclusion

Task.WhenAll is a versatile tool for asynchronous programming in C#, making it easier to write clean and efficient code when dealing with multiple concurrent tasks. Understanding how to effectively use Task.WhenAll is crucial for developers looking to harness the full potential of modern CPUs and responsive applications

--

--

sharmila subbiah
C# Programming

With over a decade of experience in the tech industry, I currently hold the position of Senior Software Engineer at Youlend.