Как правильно использовать Monitor.Wait и Pulse?

227
21 мая 2018, 12:40

Подскажите, как выполняется данный код:

lock (locker) {
    while (x == 0) {
        if (y > 0) {
            Monitor.Pulse(locker);
            break;
        }
        Monitor.Wait(locker);
    }
    // что-то еще делаем
}

Задача - синхронизация потоков.

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

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

Вопрос в следующем, когда поток выходит из состояния - Monitor.Wait, т.е. в цикле while (x == 0) x - изменится, будет ли еще раз проверяться условие if (y > 0) либо же будет исполняться следующий после цикла код?

Answer 1

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

Ниже поясняющий пример работы функций

public static void Main()
{
            object locker = new object();
    int x = 0;
    int y = 0;
    Task.Run(() =>
    {
        Thread.Sleep(2000);
        lock (locker)
        {
            Console.WriteLine("Sending a signal...");                
            Monitor.Pulse(locker);
            Console.WriteLine("The signal was sent");
            x = 1;
            Thread.Sleep(5000);
            Console.WriteLine("Sleep in lock finished");
        }
    });

    lock (locker)
    {
        while (x == 0)
        {
            if (y > 0)
            {
                Monitor.Pulse(locker);
                break;
            }
            Console.WriteLine("Wait...");
            Monitor.Wait(locker);
            Console.WriteLine("Wait finished");
        }
        Console.WriteLine("While finished");
    }
}

Вывод программы будет следующий:

Wait...
Sending a signal...
The signal was sent
Sleep in lock finished
Wait finished
While finished

Ответ на вопрос: Если Wait завершится, условие цикла при x !=0 будет false, соответственно цикл завершится и будет выполнятся код после цикла. Повторной проверки y>0 внутри цикла в этом случае невозможна!

READ ALSO
Формат пакета (передача через сокеты) [требует правки]

Формат пакета (передача через сокеты) [требует правки]

С помощью сокетов нужно переслать пакет в формате: [имя][время][сообщения]

214
Как удалить в MySql различные значения от символа до символа

Как удалить в MySql различные значения от символа до символа

К примеру есть таблица wp_posts в которой существует колонка post_content

174
Вопрос по созданию временной таблицы в POSTGRE and Sebasee

Вопрос по созданию временной таблицы в POSTGRE and Sebasee

В Sebasee можно создавать времянку сокращенным синтаксисом

198
Google ad words - как получить критерии таргетинга

Google ad words - как получить критерии таргетинга

например, добавить новый можно как то так:

188