このコードを考えると:
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
DoSomething(someString);
});
1000のスレッドすべてがほぼ同時にスポーンしますか?
このコードを考えると:
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
DoSomething(someString);
});
1000のスレッドすべてがほぼ同時にスポーンしますか?
回答:
いいえ、それは1000スレッドを開始しません-はい、使用されるスレッドの数を制限します。パラレル拡張機能を使用すると、物理的に持っているどのように多くに基づいて、コアの適切な数を使用して、既に忙しい何をしています。各コアに作業を割り当ててから、ワークスティーリングと呼ばれる手法を使用して、各スレッドが独自のキューを効率的に処理できるようにし、本当に必要な場合にのみ、高価なクロススレッドアクセスを実行します。
見ていPFXチームのブログのための負荷、それは仕事や他のトピックのすべての種類を割り当てる方法についての情報のを。
場合によっては、必要な並列度も指定できることに注意してください。
シングルコアマシンでは、コレクションのParallel.ForEachパーティション(チャンク)はいくつかのスレッド間で機能しますが、その数は考慮に入れられたアルゴリズムに基づいて計算され、 ForEachに割り当てているスレッド。そのため、ForEachの本体部分が長時間実行のIOバインド/ブロッキング関数を呼び出し、スレッドが待機する場合、アルゴリズムはより多くのスレッドを生成し、それらの間でコレクションを再分割します。スレッドがすばやく完了し、たとえば、いくつかの数値を計算するなど、IOスレッドをブロックしない場合、アルゴリズムは、アルゴリズムがスループット(各反復の平均完了時間)に最適と見なされるポイントまでスレッド数を増加(または実際に減少)します。
基本的に、すべてのさまざまなParallelライブラリ関数の背後にあるスレッドプールは、使用するスレッドの最適な数を計算します。物理プロセッサコアの数は、方程式の一部にすぎません。コアの数と生成されたスレッドの数の間に単純な1対1の関係はありません。
同期スレッドのキャンセルと処理に関するドキュメントはあまり役に立ちません。うまくいけば、MSはMSDNでより良い例を提供できるでしょう。
ボディコードは、複数のスレッドで実行するように作成する必要があり、通常のすべてのスレッドセーフティの考慮事項に加えて、フレームワークはその要因を抽象化しません...まだ。
並列を参照してください。反復ごとに1つのタスクを使用しますか?使用する「メンタルモデル」のアイデア。ただし、著者は「結局のところ、実装の詳細はいつでも変更される可能性があることを覚えておくことが重要です」と述べています。
すばらしい質問です。あなたの例では、クアッドコアプロセッサでも並列化のレベルはかなり低くなっていますが、待機していると並列化のレベルがかなり高くなる可能性があります。
// Max concurrency: 5
[Test]
public void Memory_Operations()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
monitor.Add(monitor.Count);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
次に、HTTPリクエストをシミュレートするために待機中の操作が追加されたときに何が起こるかを見てみましょう。
// Max concurrency: 34
[Test]
public void Waiting_Operations()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(1000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
私はまだ何も変更を加えておらず、並行性/並列化のレベルは劇的に跳ね上がりました。並行処理では、を使用して制限を増やすことができますParallelOptions.MaxDegreeOfParallelism
。
// Max concurrency: 43
[Test]
public void Test()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(1000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
// Max concurrency: 391
[Test]
public void Test()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(100000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
セッティングをお勧めしますParallelOptions.MaxDegreeOfParallelism
。必ずしも使用中のスレッド数を増やすわけではありませんが、適切な数のスレッドのみを開始することが保証されます。これは懸念事項のようです。
最後に、あなたの質問に答えてください。いいえ、一度にすべてのスレッドを開始することはできません。競合状態のテストなど、完全に並行して呼び出す場合は、Parallel.Invokeを使用します。
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623368346
// 636462943623368346
// 636462943623373351
// 636462943623393364
// 636462943623393364
[Test]
public void Test()
{
ConcurrentBag<string> monitor = new ConcurrentBag<string>();
ConcurrentBag<string> monitorOut = new ConcurrentBag<string>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(DateTime.UtcNow.Ticks.ToString());
monitor.TryTake(out string result);
monitorOut.Add(result);
});
var startTimes = monitorOut.OrderBy(x => x.ToString()).ToList();
Console.WriteLine(string.Join(Environment.NewLine, startTimes.Take(10)));
}