I have a method inside which there is a foreach
loop, inside the loop I call several asynchronous operations. I asked ChatGPT to do a review, and he said that you can improve performance by adding tasks to the list and calling Task.WhenAll()
.
foreach (var name in names)
{
var some = await SomeMethodAsync1(name);
await SomeMethodAsync2(name);
//...
}
Redo to:
var tasks = names.Select(async n =>
{
var some = await SomeMethodAsync1(n);
await SomeMethodAsync2(n);
//...
}).ToList();
await tasks.WhenAll();
And I don't understand why this is more productive? After all, I also call asynchronous methods with await inside Select
, then what is the difference from a simple loop?
foreach
loop awaits at each iteration. So each iteration starts after previous is done and all calls to SomeMethodAsync1
and SomeMethodAsync2
will be done separately (consecutive) for each iteration.
When you do Select
, it starts the task that represent async
lambda that you have defined and each item in array is processed in parallel now.
Now having all tasks representing the parallel work, you can use Task.WhenAll()
method to await all tasks (btw. it cannot be called as you shown, as far as I know, WhenAll
is static method on Task
class).
Here's simple example:
Console.WriteLine("NoParallel");
await NoParallel();
Console.WriteLine("Parallel");
await Parallel();
async Task NoParallel()
{
var items = Enumerable.Range(0, 10);
foreach (var item in items)
{
await AsyncMethod();
}
}
async Task Parallel()
{
var items = Enumerable.Range(0, 10);
await Task.WhenAll(items.Select(x => AsyncMethod()));
}
async Task AsyncMethod()
{
Console.WriteLine("Start async method");
await Task.Delay(50);
Console.WriteLine("End async method");
}
Which would print (indicating what is executed in parallel and what not):
NoParallel
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Start async method
End async method
Parallel
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
Start async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method
End async method