Может кто-нибудь объяснить разницу между:
Я просто не могу этого понять. Мне кажется, первые два одинаковые?





Отличный вопрос. Возможно, я ошибаюсь ... Дайте мне попробовать ... Версия №2 моего исходного ответа ... с немного большим пониманием. Спасибо, что заставили меня прочитать :)
замок (объект)
Мониторы
Использование блокировки или монитора полезно для предотвращения одновременного выполнения блоков кода, чувствительных к потокам, но эти конструкции не позволяют одному потоку передавать событие другому. Это требует синхронизации событий, которые представляют собой объекты, которые имеют одно из двух состояний, с сигналом и без сигнала, которые можно использовать для активации и приостановки потоков. Мьютекс, семафоры - это концепции уровня ОС. например, с именованным мьютексом вы можете синхронизировать несколько (управляемых) exes (гарантируя, что на машине работает только один экземпляр вашего приложения).
Мьютекс:
Семафоры (поранил мозг).
СТРАНИЦА ДЛЯ ЧТЕНИЯ - Синхронизация потоков (C#)
Вы утверждаете, что Monitor не разрешает связь, неверно; вы все еще можете Pulse и т. д. с Monitor
Ознакомьтесь с альтернативным описанием семафоров - stackoverflow.com/a/40473/968003. Думайте о семафорах как о вышибалах в ночном клубе. В клуб одновременно может входить определенное количество людей. Если клуб заполнен, никто не может войти, но как только один человек уходит, другой человек может войти.
Как указано в ECMA и как вы можете видеть из методов Reflected, оператор блокировки в основном эквивалентен
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
Из вышеупомянутого примера мы видим, что мониторы могут блокировать объекты.
Мьютексы полезны, когда вам нужна межпроцессная синхронизация, поскольку они блокируют может на строковом идентификаторе. Один и тот же строковый идентификатор может использоваться разными процессами для получения блокировки.
Семафоры похожи на мьютексы на стероидах, они разрешают одновременный доступ, обеспечивая максимальное количество одновременных доступов ». Как только предел достигнут, семафор начинает блокировать любой дальнейший доступ к ресурсу, пока один из вызывающих не освободит семафор.
Этот синтаксический сахар был немного изменен в C# 4. Ознакомьтесь с blogs.msdn.com/ericlippert/archive/2009/03/06/…
Re "Использование других классов синхронизации .Net" - некоторые из других, о которых вам следует знать:
В CCR / TPL (Параллельные расширения CTP) также есть больше (с низкими накладными расходами) блокирующих конструкций, но они будут доступны в .NET 4.0.
Итак, если мне нужна простая сигнальная связь (например, завершение асинхронной операции) - я должен Monitor.Pulse? или использовать SemaphoreSlim или TaskCompletionSource?
Используйте TaskCompletionSource для асинхронной операции. По сути, перестаньте думать о потоках и начните думать о задачах (единицах работы). Потоки являются деталью реализации и не имеют отношения к делу. Возвращая TCS, вы можете возвращать результаты, ошибки или обрабатывать отмену, и его легко комбинировать с другими асинхронными операциями (такими как async await или ContinueWith).
Дополнительное предостережение для блокировки любого общего мьютекса, который вы определили с помощью строкового идентификатора, заключается в том, что он по умолчанию будет «локальным» мьютексом и не будет совместно использоваться в сеансах в среде терминального сервера.
Добавьте к строковому идентификатору префикс «Global \», чтобы обеспечить надлежащий контроль доступа к общим системным ресурсам. Я просто столкнулся с целой кучей проблем с синхронизацией связи со службой, работающей под учетной записью SYSTEM, прежде чем я понял это.
Я бы попытался избежать "lock ()", "Mutex" и "Monitor", если вы можете ...
Ознакомьтесь с новым пространством имен System.Collections.Concurrent в .NET 4
У него есть несколько хороших поточно-безопасных классов коллекций
http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx
ConcurrentDictionary рок! у меня больше нет ручной блокировки!
Избегать блокировки, но использовать монитор? Почему?
@mafutrct Потому что вам нужно самому позаботиться о синхронизации.
О, теперь я понимаю, вы хотели избежать ВСЕХ трех упомянутых идей. Похоже, вы использовали бы Monitor, но не использовали бы блокировку / мьютекс.
Никогда не используйте System.Collections.Concurrent. Они являются основным источником состояний гонки, а также блокируют поток вызывающих объектов.
Я сделал классы и поддержку 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 эффективно используют мьютексы для выполнения блокировки / ожидания, но используют операции сравнивать и обмениваться в стиле спин-блокировки, чтобы исключить необходимость фактического выполнения жестких блокировок, если это действительно не необходимо:
Вы можете увидеть пример того, как могут быть реализованы мониторы здесь:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup
В большинстве случаев вы используете не следует блокировки (= Мониторы) или мьютексы / семафоры. Все они блокируют ожидающие потоки.
И вам определенно классы не следует использоватьSystem.Collections.Concurrent - они не поддерживают транзакции с несколькими коллекциями, а также используют блокирующую синхронизацию.
Удивительно, но .NET не имеет эффективных механизмов для неблокирующей синхронизации.
Я реализовал последовательная очередь из GCD (мир Objc/Swift) на C# - очень легкий, не блокирующий инструмент синхронизации, использующий пул потоков, с тестами.
В большинстве случаев это лучший способ синхронизировать что угодно - от доступа к базе данных (привет, sqlite) до бизнес-логики.
Эта ссылка мне очень помогла: albahari.com/threading