Ожидание перехода всех потребителей в состояние ожидания в реализации производителя и нескольких потребителей.

Задний план

Я реализовал фрагмент кода на C#, который эквивалентен производителю со многими потребителями, использующими методы ожидания и PulseAll монитора.

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

Это упрощенная реализация потребительской стороны:

lock (_lock)
{
    while (! _condition)
    {
        Monitor.Wait(_lock);
    }
}

А это упрощенная реализация на стороне производителя:

lock (_lock)
{
    _condition = true;
    Monitor.PulseAll(_lock);
}

Это хорошо известный шаблон, который довольно эффективен и хорошо работает в моем случае.

Проблема

В некоторых случаях я хотел бы, чтобы производитель ждал, прежде чем вызывать приведенный выше код производителя, пока все потребители не окажутся внутри вызова Monitor.Wait().

Концепция решения для единого потребителя

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

Концепция решения для нескольких потребителей

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

Каждый потребитель автоматически вызовет Monitor.Wait() и уменьшит значение счетчика. Также в атомарном вызове каждый потребитель будет проверять, равен ли счетчик нулю после уменьшения, и если это так, повторно инициализирует счетчик и сигнализирует производителю.

Резюме

в реализации производителя и нескольких потребителей на С# мне нужно разрешить производителю ждать, пока все потребители перейдут в состояние ожидания, прежде чем производить.

Я не нашел способ реализовать это на основе мониторов.

Я бы предпочел решение, основанное на мониторах, но если бы совершенно другой подход сделал это проще, это тоже было бы здорово.


person David Sackstein    schedule 07.11.2019    source источник
comment
Почему для производителя важно ждать, пока все потребители не перейдут в состояние ожидания?   -  person Stephen Jennings    schedule 07.11.2019
comment
Ну да, вы уже описали свое решение, которое должно работать нормально. Вам нужны переменные работающие потребители, которые обновляются в вашей блокировке потребителями в соответствии с их состоянием. Когда он достигает 0, сигнализируйте производителю. Не используйте два замка, и это должно быть довольно просто. Какая у вас проблема?   -  person Voo    schedule 07.11.2019
comment
@Voo Это не работает. Со стороны потребителя я изменил ожидание так:\   -  person David Sackstein    schedule 07.11.2019
comment
@ Дэвид Что не работает? Поддерживать точность счетчика просто (увеличивается перед рабочим обратным вызовом, уменьшается и проверяется, если 0 после этого), а затем он просто сигнализирует производителю.   -  person Voo    schedule 07.11.2019
comment
@Voo Извините, комментарий пропал до завершения. На стороне потребителя у меня теперь есть следующее: ```lock (_lock) { while (! _condition) { // добавлено _waiting++; Монитор.PulseAll(_lock); // конец Monitor.Wait(_lock); _ожидающий--; } } ``` и на стороне производителя: ``` lock (_lock) { // добавлено while (_waiting != 1) { Monitor.Wait(_lock); } // конец добавлен _condition = true; Монитор.PulseAll(_lock); } ```   -  person David Sackstein    schedule 07.11.2019
comment
@Voo Нет уценки в комментариях? Ну и эффект такой, что производитель не ждет. Я отлаживаю это сейчас.   -  person David Sackstein    schedule 07.11.2019
comment
@Voo, проблема в том, что после того, как потребитель увеличивает ожидание, он переходит в ожидание, атомарно освобождая блокировку. Он уменьшает счетчик ожидания только ПОСЛЕ выхода из режима ожидания, поэтому в этот момент счетчик может быть равен ConsumerCount, и блокировка не будет снята. Таким образом, производителю не нужно будет ждать, чтобы начать производство снова.   -  person David Sackstein    schedule 07.11.2019
comment
@David Счетчик должен быть работающим, что позволяет избежать этой проблемы;)   -  person Voo    schedule 07.11.2019
comment
Давайте продолжим это обсуждение в чате.   -  person David Sackstein    schedule 07.11.2019
comment
Похоже, вы хотите реализовать один из шаблонов в sworthodoxy.blogspot .com/2015/05/. В частности, обратите внимание на паттерн под названием «замок отряда».   -  person Jim Rogers    schedule 08.11.2019
comment
Похоже, отличный сайт. Я посмотрю. Спасибо!   -  person David Sackstein    schedule 08.11.2019


Ответы (1)


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

person Stephen Jennings    schedule 07.11.2019
comment
Спасибо @Стивен Дженнингс. Я попробовал это и не смог найти правильное место для сброса события обратного отсчета. В одном месте это вызвало взаимоблокировку, а в другом сброс сделал сигнал недействительным до того, как его дождался производитель. Можете ли вы предложить обновление кода для демонстрации идеи? - person David Sackstein; 07.11.2019
comment
Почему важно, чтобы производитель ждал, пока все потребители перейдут в состояние ожидания? Что пойдет не так, если производитель производит значение без ожидающих потребителей? - person Stephen Jennings; 08.11.2019
comment
Возможно, я неправильно проанализировал потребности. Я сейчас пересматриваю. Спасибо. - person David Sackstein; 10.11.2019