Parallel.ForEach vs Task.Run and Task.WhenAll

2019-01-04 16:33发布

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);

3条回答
老娘就宠你
2楼-- · 2019-01-04 17:16

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));
查看更多
Bombasti
3楼-- · 2019-01-04 17:17

I ended up doing this, as it felt easier to read:

  List<Task> x = new List<Task>();
  foreach(var s in myCollectionOfObject)
  {
      // Note there is no await here. Just collection the Tasks
      x.Add(s.DoSomethingAsync());
  }
  await Task.WhenAll(x);
查看更多
Viruses.
4楼-- · 2019-01-04 17:27

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

await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s)));
查看更多
登录 后发表回答