以下のコードスニペットの違いは何ですか?両方ともスレッドプールスレッドを使用しませんか?
たとえば、コレクション内のアイテムごとに関数を呼び出したい場合、
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
以下のコードスニペットの違いは何ですか?両方ともスレッドプールスレッドを使用しませんか?
たとえば、コレクション内のアイテムごとに関数を呼び出したい場合、
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
1つ目は、はるかに優れたオプションです。
Parallel.ForEachは、内部的に、を使用しPartitioner<T>
てコレクションを作業項目に分散します。アイテムごとに1つのタスクを実行するのではなく、これをバッチ処理して、関連するオーバーヘッドを削減します。
Task
2番目のオプションは、コレクション内のアイテムごとに1つをスケジュールします。結果は(ほぼ)同じになりますが、これにより、特に大規模なコレクションの場合、必要以上にオーバーヘッドが発生し、全体的な実行時間が遅くなります。
参考までに-使用するパーティショナーは、必要に応じて、 Parallel.ForEachへの適切なオーバーロードを使用して制御できます。詳細については、MSDNのカスタムパーティショナーを参照してください。
実行時の主な違いは、2番目が非同期で動作することです。これは、Parallel.ForEachを使用して次のように複製できます。
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
これを行うことで、パーティショナーを引き続き利用できますが、操作が完了するまでブロックしないでください。
「Parallel.For」オブジェクトと「Task」オブジェクトを使用して「1,000,000,000(10億)」回メソッドを実行するという小さな実験を行いました。
プロセッサ時間を測定したところ、Parallelの方が効率的であることがわかりました。Parallel.Forは、タスクを小さな作業項目に分割し、それらをすべてのコアで最適な方法で並列に実行します。多くのタスクオブジェクトを作成している間(FYI TPLは内部でスレッドプールを使用します)、各タスクのすべての実行を移動し、以下の実験から明らかなように、ボックス内により多くのストレスを作成します。
また、基本的なTPLを説明し、Parallel.Forが通常のタスクやスレッドと比較してコアをより効率的に利用する方法を示す小さなビデオを作成しましたhttp://www.youtube.com/watch?v=No7QqSc5cl8 。
実験1
Parallel.For(0, 1000000000, x => Method1());
実験2
for (int i = 0; i < 1000000000; i++)
{
Task o = new Task(Method1);
o.Start();
}
Parallel.ForEachは最適化(新しいスレッドを開始しない場合もあります)してループが終了するまでブロックし、Task.Factoryは各アイテムの新しいタスクインスタンスを明示的に作成し、終了する前に戻ります(非同期タスク)。Parallel.Foreachの方がはるかに効率的です。
私の見解では、最も現実的なシナリオは、タスクが完了するために重い操作を行う場合です。Shivprasadのアプローチは、コンピューティング自体よりもオブジェクトの作成/メモリ割り当てに重点を置いています。私は次の方法を呼び出して調査を行いました。
public static double SumRootN(int root)
{
double result = 0;
for (int i = 1; i < 10000000; i++)
{
result += Math.Exp(Math.Log(i) / root);
}
return result;
}
このメソッドの実行には約0.5秒かかります。
Parallelを使用して200回呼び出しました。
Parallel.For(0, 200, (int i) =>
{
SumRootN(10);
});
それから私は昔ながらの方法を使ってそれを200回呼びました:
List<Task> tasks = new List<Task>() ;
for (int i = 0; i < loopCounter; i++)
{
Task t = new Task(() => SumRootN(10));
t.Start();
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
最初のケースは26656msで完了し、2番目のケースは24478msで完了しました。何度も繰り返しました。毎回、2番目のアプローチはわずかに高速です。