Matten のアドバイスに従い、TPL メカニズムを使用してください。TPL でこれを行うには、さまざまな有効な方法があります。
古い学校の方法を使用してこれを行う方法に興味がある場合は、読み続けてください。一般的なパターンは、単一のシグナリング メカニズムを使用することです。for each 操作の作成WaitHandle
はまったくスケーラブルではありません。実際、このWaitHandle.WaitAll
メソッドはとにかく 64 個のハンドルしか受け入れません。
foreach (var yIndex in yRange)
{
var yclosure = yIndex;
var finished = new CountdownEvent(1);
foreach (var xIndex in xRange)
{
var xclosure = xIndex;
finished.AddCount();
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
DoSomething(yclosure, xclosure);
}
finally
{
finished.Signal();
}
}, null);
}
finished.Signal();
finished.Wait();
}
上記のパターンはCountdownEvent
、シグナリング メカニズムとして使用します。int
カウンターとInterlocked
メソッドも使用できます。このパターンを使用するときによくある間違いが 2 つあります。
メイン スレッドは、他のスレッドと同様に並列タスクであるかのように扱う必要があります。つまり、シグナルを 1 カウントで初期化する必要があります (メイン スレッドを表すため)。そしてSignal
、ループの最後で、メイン スレッドがタスクのキューイングを完了したことを通知します。このアドバイスに従わないと、次のタスクがキューに入れられる前に 1 つのタスクが終了すると、非常に微妙な競合状態が発生する可能性が生じます。1
ラムダ式または匿名デリゲートで使用するループ変数を閉じるには、別の変数を作成する必要があります。クロージャは値ではなく変数をキャプチャするため、特別なキャプチャ変数を使用しないと、タスクが想定どおりの値で動作しない可能性があることに注意してください。
1この特定のケースAddCount
では、イベントがすでに通知されている場合、実際には例外がスローされます。