ThreadPool で 3..10 スレッドを開始するシナリオがあります。各スレッドはそのジョブを実行し、ThreadPool に戻ります。すべてのバックグラウンド スレッドが終了したときにメイン スレッドで通知される可能性のあるオプションは何ですか?
現在、作成されたスレッドごとに変数を増やし、バックグラウンドスレッドが終了しようとしているときに変数を減らす自家製の方法を使用しています。これは問題なく機能しますが、より良いオプションがあるかどうか興味がありました。
ThreadPool で 3..10 スレッドを開始するシナリオがあります。各スレッドはそのジョブを実行し、ThreadPool に戻ります。すべてのバックグラウンド スレッドが終了したときにメイン スレッドで通知される可能性のあるオプションは何ですか?
現在、作成されたスレッドごとに変数を増やし、バックグラウンドスレッドが終了しようとしているときに変数を減らす自家製の方法を使用しています。これは問題なく機能しますが、より良いオプションがあるかどうか興味がありました。
変数を(スレッド間で)デクリメントすることは、で行わない限り少し危険Interlocked.Decrement
ですが、最後のスレッドがある場合(つまり、ゼロになったとき)にイベントを発生させる場合は、このアプローチで問題ありません。例外の場合にそれを失うことを避けるために、それは「最終的に」ブロックになければならないことに注意してください(さらに、プロセスを強制終了したくない)。
「ParallelExtensions」(または.NET 4.0を使用)では、Parallel.ForEach
ここでオプションを確認することもできます...これは、すべてをブロックとして実行する別の方法である可能性があります。それらすべてを手動で監視する必要はありません。
これを試してください: https://bitbucket.org/nevdelap/poolguard
using (var poolGuard = new PoolGuard())
{
for (int i = 0; i < ...
{
ThreadPool.QueueUserWorkItem(ChildThread, poolGuard);
}
// Do stuff.
poolGuard.WaitOne();
// Do stuff that required the child threads to have ended.
void ChildThread(object state)
{
var poolGuard = state as PoolGuard;
if (poolGuard.TryEnter())
{
try
{
// Do stuff.
}
finally
{
poolGuard.Exit();
}
}
}
複数の PoolGuard をさまざまな方法で使用して、スレッドがいつ終了したかを追跡し、プールが既に閉じられているときに開始されていないスレッドを処理できます。
待機するスレッドが 64 以下の場合は、次のように WaitHandle.WaitAll メソッドを使用できます。
List<WaitHandle> events = new List<WaitHandle>();
for (int i = 0; i < 64; i++)
{
ManualResetEvent mre = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(
delegate(object o)
{
Thread.Sleep(TimeSpan.FromMinutes(1));
((ManualResetEvent)o).Set();
},mre);
events.Add(mre);
}
WaitHandle.WaitAll(events.ToArray());
実行は、すべての ManualResetEvents が設定されるまで待機します。代わりに、WaitAny メソッドを使用することもできます。
WaitAny メソッドと WaitAll メソッドは実行をブロックしますが、スレッドが完了したかどうかを後で判断するために、スポーンされるタスクにリンクされた ManualResetEvents のリストまたは辞書を単純に使用できます。
現時点では、これを行うための組み込みの方法はありません。プールスレッドを使用する際の最大の問題の1つだと思います。
マークが言うように、これはParallel Extensions /.NET4.0で修正されている種類のものです。
各スレッドに個別のManualResetEventを与え、完了時にそれぞれにイベントを設定させることはできませんでした。次に、メインスレッドで、渡されたすべてのイベントを待つことができます。
セマフォを使用して、スレッドプールと同じくらい制限を設定するのはどうですか。セマフォをフェッチするメソッドがあります。これは、スレッドの開始時に呼び出され、スレッドの終了時に解放され、すべてのセマフォを取得した場合はイベントを発生させます。
Marcのソリューションは、すべてのジョブがいつ終了したかを知りたいだけで、それよりも細かい情報は必要ない場合に最適です(あなたの場合のように)。
一部のスレッドでジョブを生成し、他のスレッドで通知を受信する場合は、WaitHandleを使用できます。コードははるかに長くなります。
int length = 10;
ManualResetEvent[] waits = new ManualResetEvent[length];
for ( int i = 0; i < length; i++ ) {
waits[i] = new ManualResetEvent( false );
ThreadPool.QueueUserWorkItem( (obj) => {
try {
} finally {
waits[i].Set();
}
} );
}
for ( int i = 0; i < length; i++ ) {
if ( !waits[i].WaitOne() )
break;
}
WaitOneメソッドは、記述されているとおり、常にtrueを返しますが、一部のオーバーロードが引数としてTimeoutをとることを思い出させるために、このように記述しました。