Возможно, это слишком поздно вечером, но я не могу думать о хорошем способе сделать это.
Я запустил набор асинхронных загрузок, и я хочу ожидать, пока они все не завершаются, прежде чем программа завершается. Это приводит меня полагать, что я должен увеличить что-то, когда загрузка запускается, и постепенно уменьшите ее, когда она заканчивается. Но затем как я ожидаю, пока количество не 0 снова?
Вид семафоров работы противоположным способом в этом, которое Вы блокируете, когда нет никаких доступных ресурсов, не, когда они все доступны (блоки, когда количество 0, а не ненулевое).
Проверьте класс CountdownLatch в этой статье журнала .
Обновление: теперь он входит в состав фреймворка с версии 4.0, Класс CountdownEvent.
.Похоже, что System.Threading.WaitHandle.WaitAll может подойти:
Waits for all the elements in the specified array to receive a signal.
Ну... вы можете схватить все семафорные счетчики на основном потоке, чтобы блокировать, когда счет равен 0, а не ненулевому .
REVISED: Здесь я предположил 3 вещи:
Так что вот мое решение, пересмотренное:
Инициализирует Семафор достаточно большим счетчиком, так что вы никогда не достигнете максимума (это может быть просто 100 или просто 10 в зависимости от вашей ситуации):
var maxDownloads = 1000;
_semaphore = new Semaphore(0, maxDownloads);
Затем при каждой загрузке, начинается с WaitOne(), прежде чем начать загрузку, так что в случае выхода программы, никакие загрузки не могут начаться.
if (_semaphore.WaitOne())
/* proceeds with downloads */
else
/* we're terminating */
Затем, по завершении загрузки, освобождаем один счетчик (если бы мы его приобрели):
finally { _semaphore.Release(1); }
А затем, при событии "Выход", потребляют все счетчики на Семафоре:
for (var i = 0; i < maxDownloads; i++)
_semaphore.WaitOne();
// all downloads are finished by this point.
...
...Для каждого потока запускается Interlock.Increment a counter. И для каждого обратного вызова на финише потока, декрементируйте его.
Затем выполните цикл с Thread.Sleep(10) или что-то вроде того, пока счетчик не достигнет нуля.
.В .NET 4 для этой цели имеется специальный тип CountdownEvent.
Или вы можете построить подобную вещь самостоятельно:
const int workItemsCount = 10;
// Set remaining work items count to initial work items count
int remainingWorkItems = workItemsCount;
using (var countDownEvent = new ManualResetEvent(false))
{
for (int i = 0; i < workItemsCount; i++)
{
ThreadPool.QueueUserWorkItem(delegate
{
// Work item body
// At the end signal event
if (Interlocked.Decrement(ref remainingWorkItems) == 0)
countDownEvent.Set();
});
}
// Wait for all work items to complete
countDownEvent.WaitOne();
}