Не понимаю необходимость Monitor.Pulse () - PullRequest
23 голосов
/ 24 февраля 2012

Согласно MSDN , Monitor.Wait():

Снимает блокировку с объекта и блокирует текущий поток до тех пор, пока он не получит блокировку заново.

Однако все, что я прочитал о Wait () и Pulse (), по-видимому, указывает на то, что простого снятия блокировки в другом потоке недостаточно.Мне нужно сначала вызвать Pulse (), чтобы разбудить ожидающий поток.

Мой вопрос - почему?Потоки, ожидающие блокировки в Monitor.Enter (), просто получают его, когда он освобождается.Нет необходимости "разбудить их".Похоже, что он побеждает полезность Wait ().

например.

static object _lock = new Object();

static void Main()
{
    new Thread(Count).Start();
    Sleep(10);

    lock (_lock)
    {
         Console.WriteLine("Main thread grabbed lock");
         Monitor.Pulse(_lock) //Why is this required when we're about to release the lock anyway?
    }
}

static void Count()
{
    lock (_lock)
    { 
        int count = 0;

        while(true)
        {
            Writeline("Count: " + count++);

            //give other threads a chance every 10th iteration
            if (count % 10 == 0)
                 Monitor.Wait(_lock);
        }
    }
}

Если я использую Exit () и Enter () вместо Wait (), я могу сделать:

static object _lock = new Object();

static void Main()
{
    new Thread(Count).Start();
    Sleep(10);

    lock (_lock) Console.WriteLine("Main thread grabbed lock");
}

static void Count()
{
    lock (_lock)
    { 
        int count = 0;

        while(true)
        {
            Writeline("Count: " + count++);

            //give other threads a chance every 10th iteration
            if (count % 10 == 0)
            {
                 Monitor.Exit(_lock);
                 Monitor.Enter(_lock);
            }
        }
    }
}

Ответы [ 3 ]

35 голосов
/ 24 февраля 2012

Вы используете Enter / Exit для получения эксклюзивного доступа к блокировке.

Вы используете Wait / Pulse, чтобы разрешить совместное уведомление: я хочу дождаться, чтобы что-то произошло, поэтому я вхожу в замок и звоню Wait;код уведомления войдет в замок и вызовет Pulse.

Обе схемы связаны, но они не пытаются выполнить одно и то же.

Подумайте, как бы вы реализовалиочередь производителя / потребителя, где потребитель может сказать: «Разбуди меня, когда у тебя есть предмет для потребления» без чего-либо подобного.

9 голосов
/ 12 сентября 2014

У меня самого было то же самое сомнение, и, несмотря на некоторые интересные ответы (некоторые из них присутствуют здесь), я все еще продолжал искать более убедительный ответ.

Я думаю, что интересная и простая мысль по этому вопросу могла быbe: я могу вызвать Monitor.Wait (lockObj) в определенный момент, когда никакой другой поток не ожидает получения блокировки на объекте lockObj .Я просто хочу дождаться, когда что-то произойдет (например, состояние какого-то объекта изменится), что, как я знаю, произойдет в конечном итоге в каком-то другом потоке.Как только это условие будет выполнено, я хочу иметь возможность повторно получить блокировку, как только другой поток снимет ее блокировку.

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

То есть мне кажется интересным пытаться понять необходимость метода Monitor.Pulse , взглянув на его полезность в функционировании Monitor.Wait .Метод.

Подумайте так: «Я не хочу снимать эту блокировку и сразу же попытаться получить ее снова, потому что Я НЕ ХОЧУ быть МЕНЯ следующим потоком, чтобы получить эту блокировку. И я такжене хочу оставаться в цикле, содержащем вызов Thread.Sleep , проверяющий некоторый флаг или что-то еще, чтобы узнать, когда условие, которого я жду, было достигнуто, чтобы я мог попытаться повторно получитьблокировка. Я просто хочу «впасть в спячку» и автоматически пробудиться, как только кто-то скажет мне, что условие, которого я жду, достигнуто. ".

9 голосов
/ 24 февраля 2012

Прочитайте раздел Замечания связанной страницы MSDN:

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

Этот метод возвращает, когда вызывающий поток повторно получает блокировку объекта. Обратите внимание, что этот метод блокируется на неопределенный срок, если держатель блокировки не вызывает Pulse или PulseAll .

Так что, в основном, когда вы вызываете Monitor.Wait, ваш поток находится вочередь ожиданияДля повторного захвата блокировки он должен находиться в очереди готовности.Monitor.Pulse перемещает первый поток в очереди ожидания в очередь готовности и, таким образом, позволяет ему повторно захватить блокировку.

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