Кто-нибудь может дать мне простой пример использования System.Monitor на C#?

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

Я был бы очень благодарен, если бы команда SO могла объяснить мне эту потенциально замечательную конструкцию :-)

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
492
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот очень простой пример; вызов 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 Bannister 23.10.2008 12:47

@Jan: Как вы думаете, где именно он зайдет в тупик? подсказка: работает нормально

Marc Gravell 23.10.2008 12:50

Или добавленное мной объяснение (честно говоря, абзаца объяснения не было в первой опубликованной мной версии)

Marc Gravell 23.10.2008 12:56

Хахахаха, это точный мысленный процесс, через который я прошел, пытаясь выяснить код Monitor.Wait () / Monitor.Enter (): «Конечно, это тупик». Ключевым элементом информации является «вызов Wait снимает блокировку», чего я не знал. Спасибо, Марк!

endian 23.10.2008 12:57

@endian: нет проблем. Бит, с которым я боролся какое-то время, заключается в том, что не Pulse () перезапускает Main, а блокировку Worker релизы. Мейн все еще должен повторно получить блокировку, прежде чем она сможет продолжить.

Marc Gravell 23.10.2008 13:00

Посмотрим, поможет ли эта часть моей статьи ... (вторая половина этой страницы). Он реализует очередь производителя / потребителя: когда производитель создает что-то в очереди (и обнаруживает, что оно пусто - в качестве оптимизации), он подает импульс на монитор, чтобы разбудить любые ожидающие потоки. Когда потребитель пытается использовать данные из очереди, но обнаруживает, что она пуста, он ожидает импульса перед повторной попыткой.

В качестве альтернативы, Учебник Джо Альбахари по многопоточности также имеет раздел ожидания / импульса.

Это очень похоже на то, к чему вы привыкли - WaitHandle, хотя, честно говоря, мне легче разобраться, чем с WaitHandles, в основном потому, что он очень похож на ожидание / уведомление Java, с которым я "вырос" :)

Спасибо, Джон, я пытался выяснить это с вашего фантастического сайта в прошлом, но безуспешно (и, вероятно, без достаточных усилий).

endian 23.10.2008 12:57

В этом случае мне, очень, интересно услышать, как я могу улучшить существующее объяснение :) Если честно, прошло много времени с тех пор, как я что-то делал с потоками - это можно было сделать с капитальным ремонтом. Какие-то конкретные фрагменты объяснения Wait / Pulse, которые сбивали с толку?

Jon Skeet 23.10.2008 13:00

Другие вопросы по теме