Может кто-нибудь объяснить разницу между:
- блокировка (некоторый объект) {}
- Использование Mutex
- Используя семафор
- Использование монитора
- Использование других классов синхронизации .Net
Я просто не могу понять это. Мне кажется, первые два одинаковы?
Может кто-нибудь объяснить разницу между:
Я просто не могу понять это. Мне кажется, первые два одинаковы?
Ответы:
Отличный вопрос Возможно, я ошибаюсь .. Позвольте мне попробовать .. Редакция № 2 моего оригинального ответа ... с небольшим пониманием. Спасибо, что заставили меня прочитать :)
замок (объект)
Мониторы
Использование блокировки или монитора полезно для предотвращения одновременного выполнения чувствительных к потокам блоков кода, но эти конструкции не позволяют одному потоку передавать событие другому. Это требует событий синхронизации , которые являются объектами, которые имеют одно из двух состояний, сигнализируемых и не сигнализируемых, которые можно использовать для активации и приостановки потоков. Mutex, Семафоры - концепции уровня ОС. например, с помощью именованного мьютекса вы можете синхронизировать несколько (управляемых) exe (гарантируя, что на машине работает только один экземпляр вашего приложения).
мьютекс:
Семафоры (повредили мне мозг).
Monitor
не допускаете неправильное общение; вы все еще можете и Pulse
сMonitor
Re "Использование других классов синхронизации .Net" - некоторые другие, о которых вы должны знать:
В CCR / TPL ( CTP- файл Parallel Extensions CTP) есть и другие (с минимальными издержками) блокирующие конструкции, но в IIRC они будут доступны в .NET 4.0.
Как указано в ECMA, и как вы можете наблюдать из отраженных методов, оператор блокировки в основном эквивалентен
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
Из приведенного выше примера мы видим, что мониторы могут блокировать объекты.
Mutexe полезны, когда вам нужна межпроцессная синхронизация, поскольку они могут блокировать строковый идентификатор. Один и тот же строковый идентификатор может использоваться разными процессами для получения блокировки.
Семафоры похожи на мьютексы на стероидах, они позволяют одновременный доступ, обеспечивая максимальное количество одновременных обращений ». Как только предел достигнут, семафор начинает блокировать любой дальнейший доступ к ресурсу, пока один из вызывающих не освободит семафор.
Я сделал поддержку классов и CLR для потоков в DotGNU, и у меня есть несколько мыслей ...
Если вам не требуются межпроцессные блокировки, вы всегда должны избегать использования Mutex и семафоров. Эти классы в .NET являются обертками для Win32 Mutex и Semaphores и имеют довольно большой вес (для них требуется переключение контекста в ядро, что является дорогостоящим - особенно если ваша блокировка не конфликтует).
Как уже упоминалось, оператор C # lock - это волшебство компилятора для Monitor.Enter и Monitor.Exit (существующее в try / finally).
Мониторы имеют простой, но мощный механизм сигнала / ожидания, которого нет в мьютексах с помощью методов Monitor.Pulse / Monitor.Wait. Эквивалентом Win32 будут объекты событий через CreateEvent, которые на самом деле также существуют в .NET как WaitHandles. Модель Pulse / Wait аналогична Unix-системам pthread_signal и pthread_wait, но работает быстрее, поскольку в неконтролируемом случае они могут быть полностью операциями пользовательского режима.
Monitor.Pulse / Wait прост в использовании. В одном потоке мы блокируем объект, проверяем флаг / состояние / свойство и, если это не то, что мы ожидаем, вызывают Monitor.Wait, который снимет блокировку и будет ждать отправки импульса. Когда ожидание возвращается, мы возвращаемся назад и снова проверяем флаг / состояние / свойство. В другом потоке мы блокируем объект всякий раз, когда меняем флаг / состояние / свойство, а затем вызываем PulseAll для пробуждения любых прослушивающих потоков.
Часто мы хотим, чтобы наши классы были потокобезопасными, поэтому мы помещаем блокировки в наш код. Однако часто бывает так, что наш класс будет когда-либо использоваться только одним потоком. Это означает, что блокировки неоправданно замедляют наш код ... именно здесь умная оптимизация в CLR может помочь повысить производительность.
Я не уверен в реализации блокировок Microsoft, но в DotGNU и Mono флаг состояния блокировки хранится в заголовке каждого объекта. Каждый объект в .NET (и Java) может стать блокировкой, поэтому каждый объект должен поддерживать это в своем заголовке. В реализации DotGNU есть флаг, который позволяет вам использовать глобальную хеш-таблицу для каждого объекта, который используется в качестве блокировки - это имеет преимущество, заключающееся в устранении 4-байтовых издержек для каждого объекта. Это не очень хорошо для памяти (особенно для встраиваемых систем, которые не имеют многопоточности), но сильно ухудшает производительность.
И Mono, и DotGNU эффективно используют мьютексы для выполнения блокировки / ожидания, но используют операции сравнения и обмена в стиле спин-блокировки, чтобы исключить необходимость фактически выполнять жесткие блокировки, если в этом нет особой необходимости:
Вы можете увидеть пример того, как мониторы могут быть реализованы здесь:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
Дополнительным предостережением для блокировки любого общего Mutex, который вы определили с помощью идентификатора строки, является то, что он будет по умолчанию использовать мьютекс «Local \» и не будет использоваться совместно между сеансами в среде терминального сервера.
Добавьте к строковому идентификатору префикс «Global \», чтобы обеспечить надлежащий контроль доступа к общим системным ресурсам. Я просто столкнулся с целой кучей проблем с синхронизацией связи со службой, работающей под учетной записью SYSTEM, прежде чем я понял это.
Я хотел бы попытаться избежать "lock ()", "Mutex" и "Monitor", если вы можете ...
Ознакомьтесь с новым пространством имен System.Collections.Concurrent в .NET 4.
В нем есть несколько хороших поточно-безопасных классов коллекции.
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
ConcurrentDictionary рулит! нет ручной блокировки больше для меня!
В большинстве случаев вы не должны использовать блокировки (= мониторы) или мьютексы / семафоры. Все они блокируют текущий поток.
И вам определенно не следует использовать System.Collections.Concurrent
классы - они являются основным источником условий гонки, потому что не поддерживают транзакции между несколькими коллекциями, а также блокируют текущий поток.
Удивительно, но в .NET нет эффективных механизмов синхронизации.
Я реализовал последовательную очередь из GCD ( Objc/Swift
world) на C # - очень легкий, не блокирующий инструмент синхронизации, использующий пул потоков, с тестами.
В большинстве случаев это лучший способ синхронизировать что угодно - от доступа к базе данных (привет sqlite) до бизнес-логики.