Я стараюсь избегать этого стиля, но, как уже сказал Джон, Monitor.Wait
освобождает монитор, с которым он вызван, поэтому в этот момент блокировка отсутствует.
Но пример слегка ущербный ИМХО. Проблема, как правило, в том, что если Monitor.Pulse
вызывается до Monitor.Wait
, ожидающий поток никогда не будет сигнализировать . Имея это в виду, автор решил «играть осторожно» и использовал перегрузку, которая указала время ожидания. Таким образом, откладывая ненужное получение и снятие блокировки, код просто не кажется правильным.
Чтобы объяснить это лучше, рассмотрим следующую модификацию:
public static void tween()
{
object wait_object = new object();
Action OnComplete = () =>
{
lock (wait_object)
{
Monitor.Pulse(wait_object);
}
};
// let's say that a background thread
// finished really quickly here
OnComplete();
lock (wait_object)
{
// this will wait for a Pulse indefinitely
Monitor.Wait(wait_object);
}
}
Если OnComplete
будет вызван до того, как блокировка будет получена в основном потоке, и тайм-аут не истечет, мы получим тупик. В вашем случае Monitor.Wait
будет просто зависать некоторое время и продолжаться после истечения времени ожидания, но вы поймете, что идея.
Вот почему я обычно рекомендую более простой подход:
public static void tween()
{
using (AutoResetEvent evt = new AutoResetEvent(false))
{
Action OnComplete = () => evt.Set();
// let's say that a background thread
// finished really quickly here
OnComplete();
// event is properly set even in this case
evt.WaitOne();
}
}
Цитировать MSDN :
Класс Monitor не поддерживает состояние, указывающее, что был вызван метод Pulse. Таким образом, если вы вызываете Pulse, когда нет ожидающих потоков, следующий поток, который вызывает Wait, блокируется, как если бы Pulse никогда не вызывался. Если два потока используют Pulse и Wait для взаимодействия, это может привести к тупику.
Сравните это с поведением класса AutoResetEvent: если вы отправляете сигнал AutoResetEvent, вызывая его метод Set, и нет ожидающих потоков, AutoResetEvent остается в сигнальном состоянии до тех пор, пока поток не вызовет WaitOne, WaitAny или WaitAll. AutoResetEvent освобождает этот поток и возвращает его в состояние без сигнала.