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);
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- WithParallel.ForEach
, there is aPartitioner
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 theParallel
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:
Note that this can also be written in this shorter form:
I ended up doing this, as it felt easier to read:
The first version will synchronously block the calling thread (and run some of the tasks on it).
If it's a UI thread, this will freeze the UI.
The second version will run the tasks asynchronously in the thread pool and release the calling thread until they're done.
There are also differences in the scheduling algorithms used.
Note that your second example can be shortened to