Task.WhenAll vs Parallel.Foreach
What is the difference between Task.WhenAll and Parallel.Foreach when you have multiple tasks to run concurrently?
Both of them are a way to implement multiple concurrent task execution. But with some differences that specify where each of them fit to be used.
I’d like to start with a sample to describe this difference in practice. Suppose that we have a method comprised of one HTTP call and then one store command in the database. We want to execute multiple tasks of this request concurrently.
public async Task Foo(Request request)
{ var result = await httpService.Send(request);
await repository.Store(result);
}
So for executing multiple calls of the Foo method concurrently we can use these ways in .Net Core :
Parallel.ForEach(requests, async request =>
{
using (var sendScope = service.CreateScope())
{
var callService = sendScope.ServiceProvider.GetRequiredService<ICallService>();
await callService.Foo(request);
}
});
Also, it is possible to use Task.WhenAll for doing that:
var tasks = requests.Select(async request =>
{
using (var sendScope = service.CreateScope())
{
var callService = sendScope.ServiceProvider.GetRequiredService<ICallService>();
await callService.Call(request);
}
});await Task.WhenAll(tasks);
I have executed both of them on my laptop with the below characteristics:
Cori7 Intel 6700 HQ CPU (2.60 GHz 8 CPU), 16 GB DDR4 RAM
So I’ve captured these results from different executions:
The above picture shows how long it takes to execute in milliseconds for the specified number of Concurrent tasks of the Foo Method.
There are two important points in the above execution result :
At first, the Parallel.Foreach has a faster execution time in comparison with Task.WhenAll .
The second point is when concurrent tasks get more than 8000, Parallel.Foreach encounter with failed statues.
So, In Conclusion, Although Parallel.Foreach has faster execution time, but Task.WhenAll has higher scalability. It means that by increasing the request load the Task.WhenAll Handles it without any failure.
Don’t confuse scalability vs speed, Although Parallel.Foreach is better in speed but has lower scalability in comparison with Task.WhenAll to respond to a high load of concurrent requests.
This result comes from the fact that Task.WhenAll has designed to handle concurrent I/O bound Tasks with higher scalability as it uses asynchronous non-blocking way to share threads to handle concurrent requests.
But, on the other hand, Parallel itself is synchronous. So it is beneficial to use it in CPU bound logics to get better performance.