Вернуться к нулю CountdownEvent

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

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

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

Ответ 1

Вы писали:

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

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

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();
}

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Это имеет смысл? Я могу быть в стороне от того, что вы пытаетесь выполнить.

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

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

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

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

Ответ 7

Вы можете использовать объект Queue, чтобы добавить "work" и снова вывести его.

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

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