Комментарии 26
Mutex как правильно указали - работает на уровне ОС. Поэтому сгодится например для обеспечения запуска 1 копии приложения (можно использовать global для ограничения между пользователями\сессиями).
Пример AutoResetEvent явно показывает, что сейчас лучше работать с тасками - возвращать Task из вашего метода и await-ить его.
В нынешнем дотнете есть ещё Lazy, который часть задач на себя берёт, которые раньше решались как раз чем-то типа lock.
И картинки вместо кода, ну как так-то? Не все, кто читает вашу статью, способны воспринимать визуальную информацию. А могло бы быть весьма полезно.
Mutex как правильно указали - работает на уровне ОС. Поэтому сгодится например для обеспечения запуска 1 копии приложения (можно использовать global для ограничения между пользователями\сессиями).
Пример AutoResetEvent явно показывает, что сейчас лучше работать с тасками - возвращать Task из вашего метода и await-ить его.
В нынешнем дотнете есть ещё Lazy, который часть задач на себя берёт, которые раньше решались как раз чем-то типа lock.
Про Lazy
слышал, но кроме как в учебных примерах не пользовался, поэтому забыл. Благодарю за напоминание.
Аналог AutoResetEvent в тасках - TaskCompletionSource
AutoResetEvent используется для других совсем сценариев. Простой пример - бесконечный цикл процессинга какой-нибудь очереди. Пока очередь пуста - ивент ожидает, потоки не занимает. В методе, который ставит сообщения (или прямо готовые экземпляры обработчиков) после постановки в очередь выполняется сет, ивент срабатывает, процессинг запускается. Очередь опустела - все опять на паузе.
Согласен, такой вариант таской не заменить, таска для одноразового ожидания =)
Насколько понимаю все эти ResetEvent уже вообще не нужны давно. Для тех же очередей уже есть готовые классы
Для описанного сценария (конвейер) никаких готовых классов нету. Готовый класс очереди это обычная FIFO коллекция. Реализация конвейера через ResetEvent до сих пор наиболее оптимальна и востребована достаточно часто.
Есть и другие сценарии. ResetEvent классы используются не менее широко чем все остальные наследники иерархии WaitHandle, такие как мьютексы и семафоры. Причем и самим майкрософтом они используются, в самых свежих реализациях.
Разумеется больше они используются в десктопе и мобайле, на бэкэнде в принципе редко приходится возиться с синхронизацией потоков, а те же конвейеры реализуются на каких-нибудь месседж брокерах.
Сам семафор предоставляется ОС и используется для того, чтобы ограничить число одновременных пользователей ресурса.
Какой вывод можно из этого заключить? Что SemaphoreSlim можно использовать для межпроцессорной синхронизации?
Тут да, ошибся и не раскрыл разницу между Semaphore
и SemaphoreSlim
, прошу прощение. SemaphoreSlim
не подойдет для межпроцессорной синхронизации, для этой задачи нужно использовать именно Semaphore
.
Такой, на который фантазия позволяет - ограниченный доступ к ресурсу, например тротлинг, degree of parallelism - самые распространенные случаи использования. Если бы я писал про Interlocked - то упомянул бы про общие черты с volatile, и про ключевые отличия в этом их общем разрезе, так сказать. Речь о запрете на reordering optimization.
Не упомянуты ReaderWriterLockSlim
/ReaderWriterLock
, CountdownEvent
, volatile
/ MemoryBarrier
, не раскрыта разница между AutoResetEvent
и ManualResetEvent
. Про lock
тоже можно было бы рассказать поподробнее (что это синтаксический сахар над Monitor.Enter
/ Monitor.Exit
)
Работает на уровне ОС
Любой ОС, поддерживаемой .NET?
Сам я не проверял, но вот есть довольно свежий вопрос https://stackoverflow.com/questions/75308514/mutex-behaviour-under-linux - но там никто не ответил.
Semaphore использует Monitor.WaitHandle.
Lock использует Monitor.WaitHandle.
AutoResetEvent использует Monitor.WaitHandle.
И всё это использует объекты ядра операционки - mutex.
SemaphoreSlim тоже использует мьютексы, когда не может обойтись атомарками (interlocked). Вообще, худые семафоры - это попытка сэкономить на использовании мьютексов за счёт атомарных операций. Очень удачная, надо сказать.
Статья - безграмотная чушь.
Ну неправда же. Lock использует Monitor.Enter. И дальше я не копал, но очень сомнительно, что дальше это работает на мьютексе - объекте межпроцессной синхронизации.
Монитор устроен довольно сложно, но если опускать детали, то он сначала крутится в спинлоке на уровне юзерленда с проверкой того самого поля синхронизации в хэдере объекта, а потом, если прошло много времени, создает объект синхронизации уровня ядра типа Event и паркует тред. Это все описано у рихтера под названием "hybrid lock".
Это те вещи, которые я часто встречаю во время работы
А что сфера разработки? В бэке за 7 лет только локи иногда встречал и 1 раз AutoResetEvent, и то там тасками можно было обойтись.
Это все слишком сложно, я использую вместо этого паттерн Consumer-Producer паттер для определения количества исполнителей.
Есть ещё примитивы, о которых можно было рассказать
Синхронизация операций в .NET на примерах