Один из подходов заключается в использовании CountdownEvent
.
ExternalMethod()
{
//some calculations
var finished = new CountdownEvent(1);
for (int i = 0; i < 10; i++)
{
int capture = i; // This is needed to capture the loop variable correctly.
finished.AddCount();
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
SomeMethod(capture);
}
finally
{
finished.Signal();
}
}, null);
}
finished.Signal();
finished.Wait();
//continuation ExternalMethod
}
Если CountdownEvent
недоступен, то здесь есть альтернативный подход.
ExternalMethod()
{
//some calculations
var finished = new ManualResetEvent(false);
int pending = 1;
for (int i = 0; i < 10; i++)
{
int capture = i; // This is needed to capture the loop variable correctly.
Interlocked.Increment(ref pending);
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
SomeMethod(capture);
}
finally
{
if (Interlocked.Decrement(ref pending) == 0) finished.Set();
}
}, null);
}
if (Interlocked.Decrement(ref pending) == 0) finished.Set();
finished.WaitOne();
//continuation ExternalMethod
}
Обратите внимание, что в обоих примерах сам цикл for
обрабатывается как параллельный рабочий элемент (он находится в отдельном потоке от других рабочих элементов в конце концов), чтобы избежать действительно тонкого состояния гонки, которое может возникнуть, если первый рабочий элемент сигнализирует о событии до того, как следующий рабочий элемент будет поставлен в очередь.