Parallel.For が次のシナリオで多数のスレッドを上回ることができる理由を理解しようとしています: 並列で処理できるジョブのバッチを検討してください。これらのジョブの処理中に、新しい作業が追加される場合があり、それも処理する必要があります。解決策は次のParallel.For
ようになります。
var jobs = new List<Job> { firstJob };
int startIdx = 0, endIdx = jobs.Count;
while (startIdx < endIdx) {
Parallel.For(startIdx, endIdx, i => WorkJob(jobs[i]));
startIdx = endIdx; endIdx = jobs.Count;
}
これは、Parallel.For を同期する必要がある場合が複数回あることを意味します。パン優先グラフ アルゴリズム アルゴリズムを考えてみましょう。同期の数は非常に多くなります。時間の無駄ですよね?
昔ながらのスレッド化アプローチで同じことを試してみてください:
var queue = new ConcurrentQueue<Job> { firstJob };
var threads = new List<Thread>();
var waitHandle = new AutoResetEvent(false);
int numBusy = 0;
for (int i = 0; i < maxThreads; i++)
threads.Add(new Thread(new ThreadStart(delegate {
while (!queue.IsEmpty || numBusy > 0) {
if (queue.IsEmpty)
// numbusy > 0 implies more data may arrive
waitHandle.WaitOne();
Job job;
if (queue.TryDequeue(out job)) {
Interlocked.Increment(ref numBusy);
WorkJob(job); // WorkJob does a waitHandle.Set() when more work was found
Interlocked.Decrement(ref numBusy);
}
}
// others are possibly waiting for us to enable more work which won't happen
waitHandle.Set();
})));
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());
もちろん、Parallel.For
コードははるかにきれいですが、私が理解できないことは、さらに高速です! タスクスケジューラはそんなに良いですか?同期は取り除かれ、忙しい待機はありませんが、スレッド化されたアプローチは一貫して遅くなります(私にとって)。どうしたの?スレッド化アプローチを高速化できますか?
編集:すべての回答に感謝します。複数の回答を選択できれば幸いです。私は、実際に可能な改善も示すものを選択しました。