1. ホーム
  2. c#

[解決済み】Parallel.ForEachとTask.Run、Task.WhenAllとの比較

2022-04-15 03:39:21

質問

一連のタスクを非同期に開始するために、Parallel.ForEachとTask.Run()を使用することの違いは何でしょうか。

バージョン1:

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

バージョン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);

解決方法は?

この場合、2番目のメソッドはブロックするのではなく、非同期でタスクの完了を待ちます。

しかし、このように Task.Run をループで使用します。 Parallel.ForEach があります。 Partitioner を作成し、必要以上のタスクを作らないようにしています。 Task.Run は常に1つのアイテムにつき1つのタスクを作りますが(こうしている以上 Parallel クラスは作業をバッチ処理するので、作成するタスクの数は作業項目の総数より少なくなります。これにより、特にループ本体が1項目あたりの作業量が少ない場合、全体的なパフォーマンスが大幅に向上します。

このような場合、両方のオプションを組み合わせて記述することができます。

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

なお、このように短く書くこともできます。

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