Каковы различия между различными параметрами синхронизации потоков в С#?

Может кто-нибудь объяснить разницу между:

  • lock (someobject) {}
  • Использование Mutex
  • Использование семафора
  • Использование монитора
  • Использование других классов синхронизации .Net

Я просто не могу понять. Мне кажется, что первые два одинаковы?

Ответ 1

Отличный вопрос. Я, возможно, ошибаюсь.. Позвольте мне попробовать.. Редакция №2 моего ответа на вопрос.. с немного более глубоким пониманием. Спасибо, что заставило меня прочитать:)

lock (obj)

  • - это конструкция CLR, которая для синхронизации потоков (внутри объекта?). Обеспечивает, чтобы только один поток мог взять на себя ответственность за блокировку объекта и ввести заблокированный блок кода. Другие потоки должны ждать, пока текущий владелец откажется от блокировки, выходя из блока кода. Также рекомендуется блокировать частный объект-член вашего класса.

Мониторы

  • lock (obj) реализуется внутренне с помощью монитора. Вы должны предпочесть блокировку (obj), потому что это мешает вам разобраться, как забыть процедуру очистки. Это идиот-доказательство конструкции монитора, если вы хотите.
    Использование монитора обычно предпочтительнее над мьютексами, поскольку мониторы были разработаны специально для .NET Framework и поэтому лучше используют ресурсы.

Использование блокировки или монитора полезно для предотвращения одновременного выполнения поточно-чувствительных блоков кода, но эти конструкции не позволяют одному потоку передавать событие другому. Для этого требуются события синхронизации, которые являются объектами, которые имеют одно из двух состояний, сигнализируются и не сигнализируются, которые могут использоваться для активации и приостановки потоков. Мьютекс, Семафоры - это концепции уровня ОС. например, с именованным мьютеком, который вы могли бы синхронизировать с несколькими (управляемыми) exes (гарантируя, что на компьютере работает только один экземпляр вашего приложения.)

Мьютекс:

  • В отличие от мониторов, мьютекс может использоваться для синхронизации потоков между процессами. При использовании для межпроцессной синхронизации мьютекс называется с именем mutex, потому что он должен использоваться в другом приложении, и поэтому он не может использоваться совместно с глобальной или статической переменной. Ему должно быть присвоено имя, чтобы оба приложения могли получить доступ к одному и тому же объекту mutex. Напротив, класс Mutex является оболочкой для конструкции Win32. Хотя он более мощный, чем монитор, мьютекс требует переходов interop, которые являются более вычислительно дорогостоящими, чем те, которые требуются классу Monitor.

Семафоры (больно моему мозгу).

  • Используйте класс Semaphore для управления доступом к пулу ресурсов. Потоки входят в семафор, вызывая метод WaitOne, который наследуется от класса WaitHandle, и освобождает семафор, вызывая метод Release. Счетчик на семафоре уменьшается каждый раз, когда поток входит в семафор и увеличивается, когда поток освобождает семафор. Когда счетчик равен нулю, последующие запросы блокируют до тех пор, пока другие потоки не освободят семафор. Когда все потоки освобождают семафор, счетчик имеет максимальное значение, указанное при создании семафора. Поток может входить в семафор несколько раз. Класс Семафор не обеспечивает идентификацию потока на WaitOne или Release.. ответственность программистов не заставлять. Семафоры бывают двух типов: локальные семафоры и семафоры с именем . Если вы создаете объект Semaphore с помощью конструктора, который принимает имя, он связан с семафором операционной системы этого имени. Именованные системные семафоры отображаются во всей операционной системе и могут использоваться для синхронизации действий процессы. Локальный семафор существует только внутри вашего процесса. Он может использоваться любым потоком в вашем процессе, который имеет ссылку на локальный объект Semaphore. Каждый объект Semaphore является отдельным локальным семафором.

СТРАНИЦА ДЛЯ ПРОЧИТАНИЯ - Синхронизация потоков (С#)

Ответ 2

Re "Использование других классов синхронизации .Net" - некоторые из других, о которых вы должны знать:

  • ReaderWriterLock - позволяет использовать несколько считывателей или одного автора (не в одно и то же время)
  • ReaderWriterLockSlim - как указано выше, более низкие служебные данные
  • ManualResetEvent - затвор, который позволяет пропустить код при открытии
  • AutoResetEvent - как указано выше, но автоматически закрывается после открытия

В CCR/TPL есть еще несколько (нижних служебных) конструкций блокировки (Parallel Extensions CTP), но они будут сделаны доступно в .NET 4.0

Ответ 3

Как указано в ECMA, и, как вы можете наблюдать из Reflected methods, оператор блокировки в основном эквивалентен

object obj = x;
System.Threading.Monitor.Enter(obj);
try {
   …
}
finally {
   System.Threading.Monitor.Exit(obj);
}

В приведенном выше примере мы видим, что мониторы могут блокировать объекты.

Mutexe полезны, когда вам нужна межпроцессная синхронизация, так как они могут блокировать строковый идентификатор. Один и тот же строковый идентификатор может использоваться различными процессами для получения блокировки.

Семафоры похожи на мутексы на стероидах, они позволяют одновременный доступ, обеспечивая максимальное количество одновременного доступа ". Как только предел достигнут, семафор начинает блокировать любой дополнительный доступ к ресурсу, пока один из вызывающих абонентов не освободит семафор.

Ответ 4

Я делал классы и поддержку CLR для потоковой передачи в DotGNU, и у меня есть несколько мыслей...

Если вам не нужны перекрестные блокировки процессов, вы всегда должны избегать использования Mutex и Semafhores. Эти классы в .NET являются обертками вокруг Mutex и Семафоров Win32 и имеют довольно большой вес (для них требуется контекстный переключатель в ядро, которое дорого - особенно если ваша блокировка не находится под угрозой).

Как упоминалось выше, оператор С# lock является маской компилятора для Monitor.Enter и Monitor.Exit(существующий в try/finally).

У мониторов есть простой, но мощный механизм сигнала/ожидания, который у Mutexes отсутствует с помощью методов Monitor.Pulse/Monitor.Wait. Эквивалент Win32 будет объектами событий через CreateEvent, которые на самом деле также существуют в .NET как WaitHandles. Модель Pulse/Wait похожа на Unix pthread_signal и pthread_wait, но быстрее, потому что они могут быть полностью пользовательскими режимами в непротиворечивом случае.

Monitor.Pulse/Wait прост в использовании. В одном потоке мы блокируем объект, проверяем свойство flag/state/, и если это не то, что мы ожидаем, вызовите Monitor.Wait, который отпустит блокировку и дождитесь отправки импульса. Когда ожидание вернется, мы вернемся назад и снова проверим флаг/состояние/свойство. В другом потоке мы блокируем объект всякий раз, когда меняем свойство flag/state/, а затем вызываем 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

Ответ 5

Дополнительное предостережение для блокировки любых общих мьютексов, которые вы идентифицировали с идентификатором строки, заключается в том, что по умолчанию будет использоваться "локальный" мьютекс и не будет использоваться для сеансов в среде сервера терминалов.

Присвойте свой строковый идентификатор "Global", чтобы обеспечить правильное управление доступом к общим системным ресурсам. Я просто столкнулся с целым кучей проблем, синхронизирующих связь с сервисом, запущенным под учетной записью SYSTEM, прежде чем я это понял.

Ответ 6

Я бы постарался избежать "lock()", "Mutex" и "Monitor", если вы можете...

Проверьте новое пространство имен System.Collections.Concurrent в .NET 4
В нем есть несколько классных классов, не связанных с потоками.

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

ConcurrentDictionary скалы! для меня нет ручного блокирования!