Когда мне следует использовать AutoResetEvent и ManualResetEvent вместо Monitor.Wait()/Monitor.Pulse()?
-
03-07-2019 - |
Вопрос
Кажется, они оба преследуют одну и ту же цель.Когда бы я предпочел один другому?
Решение
Используйте события, когда у вас есть поток, ожидающий одного или всех событий, чтобы что-то сделать.
Используйте монитор, если вы хотите ограничить доступ к структуре данных, ограничив количество потоков, которые могут получить к ней доступ.
Мониторы обычно защищают ресурс, тогда как события сообщают вам, что что-то происходит, например, закрытие приложения.
Также событиям можно давать имена (см. метод OpenExisting), это позволяет использовать их для синхронизации между разными процессами.
Другие советы
На мой взгляд, лучше использовать Monitor, если можете. Monitor.Wait и Monitor.Pulse/PulseAll используются для сигнализации между потоками (как и Manual/AutoResetEvent), однако Monitor работает быстрее и не использует собственный системный ресурс.Также очевидно, что Monitor реализован в пользовательском режиме и управляется, тогда как Manual/AutoResetEvents требует переключения в режим ядра и p/invoke для собственных вызовов Win32, которые используют дескриптор ожидания.
Бывают ситуации, когда вам нужно будет использовать Manual/AutoResetEvent, например, для сигнализации между процессами, вы можете использовать именованные события, и, я думаю, для сигнализации собственных потоков в вашем приложении.
Я просто повторяю то, что прочитал эта превосходная статья о резьбе.
Стоит прочитать всю статью, однако ссылка приведет вас к разделу дескриптора ожидания, в котором подробно описываются события и отслеживается ожидание/импульс.
Вы бы использовали WaitHandle
когда вы хотите, чтобы поток отправлял или получал двоичный сигнал без необходимость критического раздела. Monitor.Wait
и Monitor.Pulse
с другой стороны требовать критический раздел.Как и большинство механизмов синхронизации в BCL, способы использования упомянутых вами двух механизмов частично совпадают.Но ни на секунду не думайте, что они выполняют одну и ту же цель.
Monitor.Wait
и Monitor.Pulse
представляют собой гораздо более примитивный механизм синхронизации, чем MRE или ARE.Фактически, вы можете построить MRE или ARE, используя только Monitor
сорт.Самая важная концепция, которую необходимо понять, заключается в том, как Monitor.Wait
и WaitHandle.WaitOne
методы различаются. Wait
и WaitOne
оба поместят нить в WaitSleepJoin
состояние, которое означает, что поток простаивает и отвечает только на Thread.Interrupt
или соответствующий Pulse
или Set
вызов.Но, и это главное отличие, Wait
покинет критическую секцию и заберет ее снова атомарным способом. WaitOne
просто не может этого сделать.Это настолько фундаментальное различие в поведении этих механизмов синхронизации, что оно определяет сценарии, в которых они могут использоваться.
В большинстве ситуаций вы бы выбрали MRE или ARE.Они удовлетворяют большинству ситуаций, когда одному потоку необходимо получить сигнал от другого.Однако, если вы хотите создать свой собственный механизм сигнализации, вам нужно будет использовать Wait
и Pulse
.Но, опять же, в .NET BCL уже реализовано большинство популярных механизмов сигнализации.Следующие сигнальные механизмы уже существуют1.
- Мануалресетевент (или мануалресетевентслим)
- AutoResetEvent
- Семафор (или SemaphoreSlim)
- СобытиеWaitHandle
- Событие обратного отсчета
- Барьер
1Почетное упоминание достаётся BlockingCollection
сорт.Это не механизм сигнализации как таковой, но он обладает качествами механизма сигнализации с дополнительным преимуществом, заключающимся в том, что к сигналу можно прикреплять данные.В этом случае сигнал означает, что элемент доступен в коллекции, а данные, связанные с этим сигналом, являются самим элементом.
В этом руководстве подробно описано, что вам нужно знать:http://www.albahari.com/threading/
В частности, это будет охватывать классы XXXResetEvent,
http://www.albahari.com/threading/part2.aspx
и это будет охватывать Wait/Pulse:http://www.albahari.com/threading/part4.aspx#_Wait_and_Pulse