Monitor.Wait, переменная условия - PullRequest
11 голосов
/ 13 июня 2011

Учитывая следующий фрагмент кода (найденный где-то во время изучения потоков).

 public class BlockingQueue<T>
    {
        private readonly object sync = new object();
        private readonly Queue<T> queue;
        public BlockingQueue()
        {
            queue = new Queue<T>();
        }

        public void Enqueue(T item)
        {
            lock (sync)
            {
                queue.Enqueue(item);
                Monitor.PulseAll(sync);
            }

        }
        public T Dequeue()
        {
            lock (sync)
            {
                while (queue.Count == 0)
                    Monitor.Wait(sync);

                return queue.Dequeue();
            }

        }
    }

Что я хочу понять, это:

Почему существует цикл while?

   while (queue.Count == 0)
            Monitor.Wait(sync);

и что не так с

 if(queue.Count == 0)
       Monitor.Wait(sync);

Фактически, все время, когда я вижу похожий код, я обнаружил, используя циклМожет кто-нибудь, пожалуйста, помогите мне понять использование одного над другим.Спасибо.

Ответы [ 5 ]

18 голосов
/ 13 июня 2011

Вы должны понимать, что делают Pulse, PulseAll и Wait.Monitor поддерживает две очереди: очередь ожидания и очередь готовности.Когда поток вызывает Wait, он перемещается в очередь ожидания.Когда поток вызывает Pulse, он перемещает один и только один поток из очереди ожидания в очередь готовности.Когда поток вызывает PulseAll, он перемещает все потоков из очереди ожидания в очередь готовности.Потоки в очереди готовности могут повторно получить блокировку в любой момент, но, конечно, только после того, как текущий держатель снимает ее.

Исходя из этих знаний, довольно легко понять, почему вы должны перепроверить счетчик очереди, когдаиспользуя PulseAll.Это связано с тем, что все потоки снятия с очереди в конце концов проснутся и захотят извлечь элемент из очереди.Но что делать, если в очереди только один элемент?Очевидно, что мы должны перепроверить количество очередей, чтобы избежать выгрузки пустой очереди.

Итак, каков был бы вывод, если бы вы использовали Pulse вместо PulseAll?Все еще будет проблема с простой проверкой if.Причина в том, что поток из очереди готовности не обязательно будет следующим потоком для получения блокировки.Это связано с тем, что Monitor не дает предпочтения вызову Wait над вызовом Enter.

Цикл while является довольно стандартным шаблоном при использовании Monitor.Wait.Это потому, что пульсация потока не имеет семантического значения сама по себе.Это всего лишь сигнал о том, что состояние блокировки изменилось.Когда потоки просыпаются после блокировки на Wait, они должны перепроверить то же условие, которое первоначально использовалось для блокировки потока, чтобы увидеть, может ли поток теперь продолжаться.Иногда он не может и поэтому должен блокировать еще немного.

Лучшее эмпирическое правило заключается в том, что если есть сомнения относительно того, использовать ли проверку if или проверку while, всегда выбирайте * 1034.* цикл, потому что это безопаснее.Фактически, я бы довел это до крайности и предложил бы всегда использовать цикл while, потому что нет никакого преимущества в использовании более простой проверки if и потому что проверка if почти всегданеправильный выбор в любом случае.Аналогичное правило справедливо для выбора: использовать Pulse или PulseAll.Если есть сомнения относительно того, какой из них использовать, всегда выбирайте PulseAll.

3 голосов
/ 13 июня 2011

вы должны продолжать проверять, пуста очередь или нет.Использование только, если бы только проверить его один раз, подождите некоторое время, а затем в очереди.Что если в это время очередь еще пуста?BANG!ошибка переполнения очереди ...

1 голос
/ 13 июня 2011

с , если условие, когда что-то сняло блокировку, queue.Count == 0 больше не будет проверять и, возможно, ошибка опустошения очереди, поэтому мы должны проверять условие каждый раз, потому что параллелизма, и это называется Spinning

0 голосов
/ 30 января 2013

Почему в Unix это может пойти не так из-за ложного пробуждения, возможного из-за сигналов ОС. Это побочный эффект, который не гарантируется никогда и на окнах. Это не наследие, это то, как работает ОС. Если мониторы реализованы в терминах условной переменной, то есть.

def: ложное пробуждение - это перепланирование спящего потока на сайте ожидания условной переменной, которое не было инициировано действием, исходящим из текущих потоков программы (например, Pulse()) .

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

0 голосов
/ 13 июня 2011
if (queue.Count == 0)  

будет делать.

Использование шаблона цикла while для контекста «ожидание и проверка условия», я думаю, является устаревшим пережитком.Поскольку не-Windows, переменные монитора не .NET могут запускаться без фактического Pulse.

. В .NET ваша личная переменная монитора не может быть запущена без заполнения Queue, поэтому вам не нужно беспокоитьсяо потере очереди после ожидания монитора.Но это действительно неплохая привычка - использовать цикл while для «ожидания и проверки состояния».

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