Может кто-нибудь объяснить разницу между:
- замок (какой-то объект) {}
- Использование Mutex
- Использование семафора
- Использование монитора
- Использование других классов синхронизации .Net
Я просто не могу этого понять. Мне кажется, первые два одинаковые?
Может кто-нибудь объяснить разницу между:
Я просто не могу этого понять. Мне кажется, первые два одинаковые?
Отличный вопрос. Возможно, я ошибаюсь ... Дайте мне попробовать ... Версия №2 моего исходного ответа ... с немного большим пониманием. Спасибо, что заставили меня прочитать :)
блокировка (объект)
Мониторы
Использование блокировки или монитора полезно для предотвращения одновременного выполнения блоков кода, чувствительных к потокам, но эти конструкции не позволяют одному потоку передавать событие другому. Для этого требуются события синхронизации, которые представляют собой объекты, которые имеют одно из двух состояний, сигнализируемое и несигнальное, которые можно использовать для активации и приостановки потоков. Мьютекс, семафоры - это концепции уровня ОС. например, с именованным мьютексом вы можете синхронизировать несколько (управляемых) exes (гарантируя, что на машине работает только один экземпляр вашего приложения).
Мьютекс:
семафоры (больно мои головной мозг).
СТРАНИЦА ДЛЯ ЧТЕНИЯ - Синхронизация потоков (C #)
Monitor не разрешает общение неверно; вы все еще можете Pulse и т. д. с Monitor
- person Marc Gravell; 22.07.2009
Re "Использование других классов синхронизации .Net" - некоторые из других, о которых вам следует знать:
В CCR / TPL также есть больше (с низкими накладными расходами) блокирующих конструкций (Parallel Extensions CTP), но IIRC, они будут доступны в .NET 4.0.
Как указано в ECMA и как вы можете видеть из методов Reflected, оператор блокировки в основном эквивалентен
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
Из вышеупомянутого примера мы видим, что мониторы могут блокировать объекты.
Мьютексы полезны, когда вам нужна межпроцессная синхронизация, поскольку они могут заблокировать строковый идентификатор. Один и тот же строковый идентификатор может использоваться разными процессами для получения блокировки.
Семафоры похожи на мьютексы на стероидах, они разрешают одновременный доступ, обеспечивая максимальное количество одновременных доступов ». Как только предел достигнут, семафор начинает блокировать любой дальнейший доступ к ресурсу, пока один из вызывающих не освободит семафор.
Я сделал классы и поддержку CLR для потоковой передачи в DotGNU, и у меня есть несколько мыслей ...
Если вам не требуются межпроцессные блокировки, вам всегда следует избегать использования мьютексов и семафоров. Эти классы в .NET являются оболочками для Win32 Mutex и Semaphores и имеют довольно большой вес (они требуют переключения контекста в ядро, что является дорогостоящим - особенно если ваша блокировка не оспаривается).
Как уже упоминалось, оператор блокировки C # - это магия компилятора для Monitor.Enter и Monitor.Exit (существующая в try / finally).
У мониторов есть простой, но мощный механизм «сигнал / ожидание», которого нет у мьютексов с помощью методов Monitor.Pulse / Monitor.Wait. Эквивалентом Win32 будут объекты событий через CreateEvent, которые на самом деле также существуют в .NET как WaitHandles. Модель Pulse / Wait похожа на pthread_signal и pthread_wait в Unix, но работает быстрее, потому что они могут быть полностью операциями пользовательского режима в неконкурентном случае.
Monitor.Pulse / Wait прост в использовании. В одном потоке мы блокируем объект, проверяем флаг / состояние / свойство и, если это не то, что мы ожидаем, вызываем Monitor.Wait, который снимает блокировку и ждет, пока не будет отправлен импульс. Когда ожидание возвращается, мы возвращаемся в цикл и снова проверяем флаг / состояние / свойство. В другом потоке мы блокируем объект всякий раз, когда меняем флаг / состояние / свойство, а затем вызываем PulseAll, чтобы разбудить любые прослушивающие потоки.
Часто мы хотим, чтобы наши классы были потокобезопасными, поэтому мы устанавливаем блокировки в наш код. Однако часто бывает, что наш класс будет использоваться только одним потоком. Это означает, что блокировки излишне замедляют наш код ... именно здесь умная оптимизация в среде CLR может помочь повысить производительность.
Я не уверен в реализации блокировок Microsoft, но в DotGNU и Mono флаг состояния блокировки хранится в заголовке каждого объекта. Каждый объект в .NET (и Java) может стать блокировкой, поэтому каждый объект должен поддерживать это в своем заголовке. В реализации DotGNU есть флаг, который позволяет вам использовать глобальную хеш-таблицу для каждого объекта, который используется в качестве блокировки - это дает преимущество в устранении 4-байтовых накладных расходов для каждого объекта. Это не очень хорошо для памяти (особенно для встроенных систем, не использующих много потоков), но снижает производительность.
И Mono, и DotGNU эффективно используют мьютексы для выполнения блокировки / ожидания, но используют стиль спин-блокировки compare-and- exchange, чтобы избавиться от необходимости выполнять жесткие блокировки, если в этом нет необходимости:
Вы можете увидеть пример того, как могут быть реализованы мониторы здесь:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
Дополнительное предупреждение для блокировки любого общего мьютекса, который вы определили с помощью строкового идентификатора, заключается в том, что он по умолчанию будет «локальным» мьютексом и не будет совместно использоваться в сеансах в среде терминального сервера.
Добавьте к строковому идентификатору префикс «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) до бизнес-логики.