получать уведомления, когда все фоновые потоки

У меня есть сценарий, когда я запускаю 3..10 темы с ThreadPool. Каждый поток выполняет свою работу и возвращается в ThreadPool. Какие возможные варианты уведомления в главном потоке, когда все фоновые потоки закончили?

В настоящее время я использую доморощенный метод с увеличением переменной для каждого из созданных потоков и уменьшением ее, когда фоновый поток собирается закончить. Это работает просто отлично, но мне было любопытно, есть ли лучшие варианты.

11.12.2008 08:58:57
7 ОТВЕТОВ
РЕШЕНИЕ

Уменьшение переменной (между потоками) немного рискованно, если с ней не покончено Interlocked.Decrement, но такой подход должен подойти, если последний поток (т. Е. Когда он достигает нуля) вызывает событие. Обратите внимание, что он должен находиться в блоке finally, чтобы не потерять его в случае исключений (плюс вы не хотите прерывать процесс).

В «Параллельных расширениях» (или в .NET 4.0) вы также можете посмотреть Parallel.ForEachпараметры здесь ... это может быть еще один способ сделать все как блок. Без необходимости смотреть их все вручную.

11
11.12.2008 09:05:39
Привет Марк. Я делаю это безопасным способом, используя ключевое слово Interlocked или lock. Работает нормально и неблокирующим образом. Мне было интересно, есть ли встроенные примитивы для этого. Спасибо.
Valentin Vasilyev 11.12.2008 09:07:59

В настоящее время нет встроенного способа сделать это - я считаю это одной из самых больших проблем при использовании потоков пула.

Как говорит Марк, такие вещи исправляются в Parallel Extensions / .NET 4.0.

2
11.12.2008 09:09:51

Не могли бы вы дать каждому потоку отдельный ManualResetEvent и каждый установить событие, когда закончите. Затем в главном потоке вы можете дождаться всех прошедших событий.

1
11.12.2008 10:11:04

Решение Марка лучше всего, если вы просто хотите знать, когда все работы завершены, и вам не нужна более точная информация, чем эта (как кажется, ваш случай).

Если вы хотите, чтобы какой-то поток порождал задания, а другой - для получения уведомлений, вы можете использовать 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 в качестве аргумента.

0
11.12.2008 10:15:10

Если ждать не более 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, связанный с задачей, которая порождается для последующего определения, завершен ли поток.

3
11.12.2008 11:27:19
Этот метод прекрасно работает, если Main () не является однопоточным ([STAThread]).
Miguel Moll 28.11.2012 21:59:51
Ух ты, этот вопрос задан ... ответу более 4 лет ... насколько я помню, в WaitHandle.WaitAll есть ограничение, которое выдает исключение, если список дескрипторов содержит более 64 элементов. Может быть, это устарело сегодня.
Oliver Friedrich 19.02.2015 12:05:23

Как насчет использования Semaphore, и установить для него ограничение в той же степени, что и ваш пул потоков. Иметь метод для извлечения семафора, который будет вызываться при запуске потока, освобождать его по окончании потока и вызывать событие, если вы заняли весь семафор.

0
12.12.2008 11:55:28

Попробуйте это: 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();
        }
    }
}

Несколько PoolGuards могут по-разному использоваться для отслеживания окончания потоков и обработки потоков, которые не были запущены, когда пул уже закрыт.

4
3.12.2011 07:35:47
Статья больше не доступна. Что такое класс PoolGuard? не могли бы вы скопировать полный код?
Niloofar 11.05.2015 15:26:59