Parallel.ForEach vs Task.Run and Task.WhenAll

Petter T picture Petter T · Sep 30, 2013 · Viewed 90k times · Source

What are the differences between using Parallel.ForEach or Task.Run() to start a set of tasks asynchronously?

Version 1:

List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
    DoSomething(s);
});

Version 2:

List<string> strings = new List<string> { "s1", "s2", "s3" };
List<Task> Tasks = new List<Task>();
foreach (var s in strings)
{
    Tasks.Add(Task.Run(() => DoSomething(s)));
}
await Task.WhenAll(Tasks);

Answer

Reed Copsey picture Reed Copsey · Sep 30, 2013

In this case, the second method will asynchronously wait for the tasks to complete instead of blocking.

However, there is a disadvantage to use Task.Run in a loop- With Parallel.ForEach, there is a Partitioner which gets created to avoid making more tasks than necessary. Task.Run will always make a single task per item (since you're doing this), but the Parallel class batches work so you create fewer tasks than total work items. This can provide significantly better overall performance, especially if the loop body has a small amount of work per item.

If this is the case, you can combine both options by writing:

await Task.Run(() => Parallel.ForEach(strings, s =>
{
    DoSomething(s);
}));

Note that this can also be written in this shorter form:

await Task.Run(() => Parallel.ForEach(strings, DoSomething));