The C# method Task.WhenAll can run a bunch of async methods in parallel and returns when every one finished.
But how do you collect the return values?
UPDATED 2023-02-15: Updated code based on comments. Thx for all the suggestions.
Imagine that you have this pseudo-async-method:
private async Task<int> GetAsync(int number)
{
await Task.Delay(number * 100);
return number;
}
And you wish to call that method 20 times, and then collect all the results in a list?
That is a 3 step rocket:
- Create a list of tasks to run
- Run the tasks in parallel using Task.WhenAll.
- Iterate over the results
// Create a list of tasks to run
List<Task<int>> tasks = new List<Task<int>>();
for (int i = 0; i < 20; i++)
{
tasks.Add(GetAsync(i));
}
// Run the tasks in parallel, and
// wait until all have been run
IEnumerable<int> results = await Task.WhenAll(tasks);
// Iterate over the results
foreach (var result in results)
{
Console.WriteLine(result);
}
WHY DID I UPDATE THE CODE?
In my previous example, I forgot to include the return type when creating the list of tasks:
// WRONG:
// I did this:
List<Task> tasks = new List<Task>();
// CORRECT:
// But I should have done this:
List<Task<int>> tasks = new List<Task<int>>();
When you do not include the type that the task returns, the Task.WhenAll returns void, and you need another loop to collect the return types directly from the tasks themselves.
Thanks for all the comments. And happy coding.
MORE TO READ:
- Task.WhenAll from microsoft
- Run tasks in parallel using .NET Core, C# and async coding by briancaos
- Using C# HttpClient from Sync and Async code by briancaos
- Awaiting multiple Tasks with different results from stackoverflow
- Get the result of multiple tasks in a ValueTuple and WhenAll by Gérald Barré
- Using Task.WhenAny And Task.WhenAll by Hamid Mosalla
- Run parallel tasks in batches using .NET Core, C# and Async coding by briancaos
// This is a bit nicer (and shorter) I think
// Create a list of tasks to run
List<Task> tasks = new List<Task>();
foreach (int i=0;i<20;i++)
{
tasks.Add(GetAsync(i));
}
// Run the tasks in parallel, and
// wait until all have been run
// collecting the results at the same time
var results = await Task.WhenAll(tasks);
// the above is an array, but if a list is required this line could be used instead:
// var results = (await Task.WhenAll(tasks)).ToList();
LikeLike
// This is a bit nicer (and shorter) I think – my previous posting
// looked like the Task lost its generic parameter, string
// Create a list of tasks and start them running
List<Task> tasks = new List<Task>();
foreach (int i=0;i<20;i++)
{
tasks.Add(GetAsync(i));
}
// Wait until the running tasks have been run
// collecting the results at the same time
var results = await Task.WhenAll(tasks);
// the above is an array, but if a list is required this line could be used instead:
// var results = (await Task.WhenAll(tasks)).ToList();
LikeLike
Task.WhenAll does not run tasks!!
But for collecting all the results in one array, it is fine.
var results = await Task.WhenAll(tasks);
LikeLike
I like this without loop an new list if error (int.MinValue) exist
int someError = (from err in ListTasks
let CathError = ((Task)err).Result
where CathError == int.MinValue
select err
LikeLike
I am sorry i was using Count() and i was forgrget write all code .
int someError = (from err in ListTasks
let CathError = ((Task)err).Result
where CathError == int.MinValue
select err).Count()
LikeLike
Others have pointed this out already, but I’d like to drive the point home:
_await_ in combination with Task.WhenAll returns an array, so why on earth would you go out of your way to create a list?
I prefer not to use a specific type to var, so the easy and efficient way to do this would be to declare and assign an array:
string[] results = await Task.WhenAll(tasks);
LikeLike