Я нахожу System.Monitor очень запутанным, хотя я понимаю многопоточность, блокировки, взаимоблокировки, условия гонки, философов-обедов и все такое прочее. Обычно я использую ManualResetEvent () для координации между потоками, но я знаю, что это тяжелый объект ядра и что System.Monitor (Enter / Pulse и т. д.) Намного эффективнее. Я гуглил и гуглил, но не могу найти разумного примера.
Я был бы очень благодарен, если бы команда SO могла объяснить мне эту потенциально замечательную конструкцию :-)





Вот очень простой пример; вызов Wait снимает блокировку (позволяя Worker получить ее) и добавляет поток Main в очередь ожидания объекта блокировки. Worker затем получает блокировку и вызывает Pulse: это перемещает поток Main в очередь готовности объекта блокировки. Когда Workerрелизы блокируется, Main может возобновить работу.
Обратите внимание, что lock(obj) {...} - это просто конфетка компилятора для Monitor.Enter / Monitor.Exit в блоке try / finally.
[редактировать: я изменил образец, чтобы переместить lock(sync) ранее, чтобы избежать (маловероятного) риска пропущенного импульса]
static void Main()
{
object sync = new object();
lock (sync)
{
ThreadPool.QueueUserWorkItem(Worker, sync);
Console.WriteLine("Main sleeping");
// wait for the worker to tell us it is ready
Monitor.Wait(sync);
Console.WriteLine("Main woke up!");
}
Console.WriteLine("Press any key...");
Console.ReadKey();
}
static void Worker(object sync)
{
Console.WriteLine("Worker started; about to sleep");
Thread.Sleep(5000);
Console.WriteLine("Worker about pulse");
lock (sync)
{ // notify Main that we did something interesting
Monitor.Pulse(sync);
Console.WriteLine("Worker pulsed; about to release lock");
}
Console.WriteLine("Worker all done");
}
@Jan: Как вы думаете, где именно он зайдет в тупик? подсказка: работает нормально
Или добавленное мной объяснение (честно говоря, абзаца объяснения не было в первой опубликованной мной версии)
Хахахаха, это точный мысленный процесс, через который я прошел, пытаясь выяснить код Monitor.Wait () / Monitor.Enter (): «Конечно, это тупик». Ключевым элементом информации является «вызов Wait снимает блокировку», чего я не знал. Спасибо, Марк!
@endian: нет проблем. Бит, с которым я боролся какое-то время, заключается в том, что не Pulse () перезапускает Main, а блокировку Worker релизы. Мейн все еще должен повторно получить блокировку, прежде чем она сможет продолжить.
Посмотрим, поможет ли эта часть моей статьи ... (вторая половина этой страницы). Он реализует очередь производителя / потребителя: когда производитель создает что-то в очереди (и обнаруживает, что оно пусто - в качестве оптимизации), он подает импульс на монитор, чтобы разбудить любые ожидающие потоки. Когда потребитель пытается использовать данные из очереди, но обнаруживает, что она пуста, он ожидает импульса перед повторной попыткой.
В качестве альтернативы, Учебник Джо Альбахари по многопоточности также имеет раздел ожидания / импульса.
Это очень похоже на то, к чему вы привыкли - WaitHandle, хотя, честно говоря, мне легче разобраться, чем с WaitHandles, в основном потому, что он очень похож на ожидание / уведомление Java, с которым я "вырос" :)
Спасибо, Джон, я пытался выяснить это с вашего фантастического сайта в прошлом, но безуспешно (и, вероятно, без достаточных усилий).
В этом случае мне, очень, интересно услышать, как я могу улучшить существующее объяснение :) Если честно, прошло много времени с тех пор, как я что-то делал с потоками - это можно было сделать с капитальным ремонтом. Какие-то конкретные фрагменты объяснения Wait / Pulse, которые сбивали с толку?
Я не думаю, что это сработает, похоже, вы заблокируете синхронизацию