Правильный способ просмотра Monitor.Wait
и Monitor.Pulse
/PulseAll
не так служит средством ожидания, а (для Wait
) как средство давая знать системе, что код находится в цикле ожидания, который не может выйти пока что что-то интересное не изменится, и (для Pulse
/PulseAll
) в качестве средства, позволяющего системе знать, что код только что изменил что-то, что может привести к тому, что условие выхода приведет к очередному циклу другого потока. Нужно иметь возможность заменить все вхождения Wait
на Sleep(0)
и все еще иметь код правильно (даже если он намного менее эффективно, в результате того, что время CPU постоянно меняет условия тестирования, которые не изменились).
Для того чтобы этот механизм работать, необходимо, чтобы избежать возможности следующей последовательности:
код в цикле ожидания проверяет состояние, когда оно не выполняется.
Код в другой цепочке изменяет состояние так, чтобы оно выполнялось.
Код в этом другом потоке блокирует замок (которого еще никто не ждет).
Код в цикле ожидания выполняет Wait
, так как его состояние не было выполнено.
Wait
метод требует, чтобы нить ожидания иметь блокировку, так как это единственный способ, которым это может быть уверено, что состояние его ждет после не изменится между временем это испытанное и временем код выполняет Wait
. Метод Pulse
требует блокировки, потому что это единственный способ убедиться, что если другой поток сам «совершил» для выполнения Wait
, то Pulse
не будет происходить до тех пор, пока другой поток на самом деле этого не сделает. Обратите внимание, что использование Wait
в замке не гарантирует правильность его использования, но нет способа, чтобы использование Wait
вне замка могло быть правильным.
Wait
/Pulse
дизайн действительно работает достаточно хорошо, если обе стороны сотрудничают. Наибольшие недостатки конструкции IMHO заключаются в следующем: (1) нет механизма для потока, чтобы ждать, пока какой-либо из нескольких объектов не будет пульсирован; (2) даже если один из них «выключает» объект таким образом, что все будущие циклы ожидания должны немедленно выйти (возможно, путем проверки флажка выхода), единственный способ обеспечить, чтобы любой Wait
, которому был передан поток, получит Pulse
заключается в приобретении замка, возможно, на неопределенный срок, чтобы он стал доступен.
Почему вы предпочитаете это? как бы вы синхронизировали монитор? просто блокировка на объекте шкафчика, используемого для монитора? doen't блокировки добавляет другое переключение контекста, которое ResetEvents не нужно? – user437631
@ user437631: Да, нормальный оператор 'lock' в порядке. Это может потребовать или не потребовать дополнительного контекстного переключателя - и я не думаю, что у вас есть доказательства того, что ResetEvents не потребует этого. Фактически, поскольку они являются внутренними объектами CLR, а не потенциально перекрестно обрабатывают объекты Win32, мониторы легче ожидают, чем ResetEvents. –