200

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

4 に答える 4

203

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

ただし、ループで使用することには欠点がありますTask.RunParallel.ForEachには、Partitioner必要以上のタスクを作成しないようにするために作成される があります。 Task.Runアイテムごとに常に 1 つのタスクが作成されますが (これを行っているため)、Parallelクラスのバッチが機能するため、作成するタスクは作業アイテムの合計よりも少なくなります。これにより、特にループ本体の項目ごとの作業量が少ない場合に、全体的なパフォーマンスが大幅に向上します。

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

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

これは、次の短い形式でも記述できることに注意してください。

await Task.Run(() => Parallel.ForEach(strings, DoSomething));
于 2013-09-30T20:17:35.553 に答える
44

最初のバージョンは、呼び出し元のスレッドを同期的にブロックします (そして、いくつかのタスクを実行します)。
UI スレッドの場合、UI がフリーズします。

2 番目のバージョンは、タスクをスレッド プールで非同期に実行し、完了するまで呼び出し元のスレッドを解放します。

使用されるスケジューリング アルゴリズムにも違いがあります。

2番目の例は次のように短縮できることに注意してください

await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s))));
于 2013-09-30T20:15:05.183 に答える
0

読みやすく感じたので、私はこれをやった:

  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);
于 2018-04-14T02:21:21.460 に答える