Возврат к нулю - PullRequest
       34

Возврат к нулю

13 голосов
/ 08 декабря 2010

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

Я могу инициализировать обратный отсчетсобытие с 0 начальным счетом, но когда я пытаюсь добавить к счету, я получаю InvalidOperationException"CountdownEvent_Increment_AlreadyZero".

Есть ли альтернативный класс или другой способ, которым я могу использовать событие обратного отсчета для того, чтобыизбежать этого ограничения?

Ответы [ 8 ]

6 голосов
/ 03 января 2011

Вы писали:

Я выполняю операцию, которая создаст неизвестное число дочерних операций (не задач или потоков)

Так что же это?Вы должны сделать что-то вроде этого:

CountdownEvent ev;  
public void foo() {
    ev = new CountdownEvent(1);
    foreach ( <task in tasks_to_start> ) {
         ev.AddCount();
         // enter code here which starts your task
    }
    ev.Signal();
    ev.Wait();
}

public static void youtTask(CountdownEvent ev) {
    // some work
    // ...
    // after all is done
    ev.Signal();
}
3 голосов
/ 08 декабря 2010

Если вы можете использовать .NET 4.0 или Reactive Extensions для .NET 3.5 (который имеет бэкпорт функций .NET 4 TPL), вы можете проверить класс Barrier ,Это позволяет координировать несколько параллельных задач, чтобы они не продолжались до тех пор, пока все участники барьера не оповестят о своем прибытии.Это также должно соответствовать вашему требованию, чтобы участники появлялись и исчезали в ходе обработки.

2 голосов
/ 08 декабря 2010

Так что, по сути, вам нужен «включатель / выключатель», а не объект синхронизации, который можно настроить с произвольным обратным отсчетом.CountdownEvent не подходит для таких случаев.

Почему бы вам просто не использовать Semaphore с начальным счетом один?

1 голос
/ 18 декабря 2012

Ваш вопрос выглядит как обычная техника разветвления. Каждый раз, когда вы бы повторили, вместо этого вы запускаете другую параллельную операцию (ставьте ее в очередь потоков и т. Д.). Но вам нужно дождаться окончания всех ветвей в конце. Просто добавьте 1 к событию обратного отсчета для каждой подоперации, которую вы запускаете, и сообщите об этом в конце каждой подоперации. Это безопасно делать до тех пор, пока вы настроите алгоритм, поэтому он не будет сигнализировать, пока не добавит для каждой дочерней операции.

Я должен добавить, что вам не нужно знать заранее счет, просто сделайте 1 в корне, и каждый раз, когда вы переходите к ребенку, добавляйте 1, а затем сигнализируйте на конец каждого, и он будет динамически обрабатывать любое дерево без предварительной оплаты.

В CountdownEvent есть метод Add, который позволяет увеличивать счет в полете.

Это имеет смысл? Возможно, я не в курсе того, что вы пытаетесь достичь.

Однако, если вы действительно хотите CountdownEvent, который ведет себя так, как вы указали, довольно просто обернуть пару взаимосвязанных операций в классе, чтобы сделать то, что вы говорите.

Тем не менее, CountdownEvent создан так, чтобы быть легковесным, он почти бесплатен, если никто не ждет, пока не будет подан сигнал. В дорогом случае это оптимально, независимо от того, сколько задач (и т. Д.) Ему потребуется только для одного перехода ядра в сигнал и одной для ожидания, в худшем случае.

Для реализации того, что вы предлагаете, потребуется синхронизация вокруг сигнализации и сброса события. Событие обратного отсчета основывается на одном простом принципе, только переход от ненулевого к нулю при вызове Сигнала может сигнализировать о событии. Расы не существует, поскольку более чем один поток не может изменить значение за один раз (он заблокирован), поэтому только один поток может попытаться сигнализировать объект события (который пробуждает другой ожидающий поток). Идеально подходит.

Однако, если у вас есть несколько потоков, которые устанавливают и сбрасывают его, вам нужно синхронизировать все настройки и сбрасывать, так как счетчик может колебаться пару раз, и несколько потоков будут одновременно пытаться установить или сбросить событие. (Установка, сброс и ожидание события - все это дорого, потому что все они должны выполнить переход ядра и вызвать переключение контекста). Это не сработает, если вы не синхронизируете что-то, чтобы защитить переходы установки / сброса. Если бы они добавили это в CountdownEvent, это больше не было бы почти оптимальным, это было бы значительно дороже.

1 голос
/ 16 апреля 2011

Будет ли это работать для вас?http://msdn.microsoft.com/en-us/library/dd384749.aspx

Редактировать
Извините, что расплывчато.Используя ответ SOReader, где у каждого родителя есть событие обратного отсчета, начиная с 1, затем используя TryAddCount для дочерних приращений, а затем уменьшая родительские значения до 1, затем в родительском уменьшении с 1 до нуля, когда дети завершены, и, наконец, уменьшите счетродитель родительского потока.Итак, древовидная серия отсчетов событий.

У меня нет опыта работы с многопоточностью, но на первый взгляд я бы попробовал.

0 голосов
/ 03 октября 2018

Я сталкиваюсь с той же проблемой , но в контексте Barrier.

На самом деле Если вы думаете об этом, CountdownEvent - это одна фаза Barrier.

Итак, чтобы избежать ограничения:

Я могу инициализировать событие обратного отсчета с 0 начальным счетом, но когда я пытаюсь добавить к счетчику, я получаю InvalidOperationException "CountdownEvent_Increment_AlreadyZero".

Вы можете просто перейти к Barrier и использовать его AddParticipant или RemoveParticipan методы.

0 голосов
/ 15 марта 2011

Вы можете использовать объект Queue, чтобы добавить «работу» и вынуть снова.

Только когда очередь пуста, вы идете дальше.

Но да, нам здесь понадобятся подробности ...

0 голосов
/ 08 декабря 2010

Как насчет семафоров: http://msdn.microsoft.com/en-us/library/system.threading.semaphore.aspx

Редактировать: следующий пост обсуждает, почему то, что вы описываете, не рекомендуется, а также предлагает обходной путь: http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/aa49f92c-01a8-4901-9846-91bc1587f3ae

...