2008-11-19 2 views
150

Может кто-то объяснить разницу между:В чем разница между различными параметрами синхронизации потоков в C#?

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

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

+0

Эта ссылка помогло мне: http://www.albahari.com/threading/ – Raphael 2014-01-08 12:45:49

ответ

123

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

замок (OBJ)

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

Мониторы

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

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

мьютекса (обеспечение, что только один экземпляр приложения выполняется на машине.):

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

Semaphores (ушиб головного мозга).

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

THE PAGE TO READ - Thread Synchronization (C#)

+17

Вы утверждаете, что `Monitor` не допускает связи неверен; вы все еще можете `Pulse` и т. д. с помощью` Monitor` – 2009-07-22 09:15:57

+2

Посмотрите другое описание Семафоров - http://stackoverflow.com/a/40473/968003. Подумайте о семафорах как вышибалы в ночном клубе. Есть определенное количество людей, которые разрешены в клубе сразу. Если клуб заполнен, никто не может войти, но как только один человек покинет другого человека, он может войти. – 2015-07-13 01:25:08

13

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

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

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

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

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

+4

Этот синтаксический сахар слегка изменился в C# 4 Ознакомьтесь с http://blogs.msdn.com/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx – 2010-05-18 12:03:30

27

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

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

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

+0

So если я хочу простую сигнальную связь (скажем, завершение async op) - мне должен Monitor.Pulse? или использовать SemaphoreSlim или TaskCompletionSource? – Vivek 2014-01-10 17:08:57

9

Дополнительная оговорка о блокировке на любом совместном Mutex, идентифицированном с идентификатором строки, заключается в том, что она по умолчанию будет использовать «Локальный» мьютекст и не будет использоваться для сеансов в терминальной серверной среде.

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

5

Я хотел бы попробовать, чтобы избежать «блокировки()», «мьютекс» и «Монитор», если вы можете ...

Заканчивать новое пространство имен System.Collections.Concurrent в .NET 4
Он имеет некоторые хорошие потокобезопасные коллекция классов

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

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

11

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

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

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

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

Monitor.Pulse/Wait прост в использовании. В одном потоке мы блокируем объект, проверяем флаг/состояние/свойство, и если это не то, что мы ожидаем, вызовите Monitor.Wait, который отпустит блокировку и дождитесь отправки импульса. Когда ожидание вернется, мы вернемся назад и снова проверим флаг/состояние/свойство. В другом потоке мы блокируем объект всякий раз, когда мы меняем свойство flag/state /, а затем вызываем PulseAll, чтобы разбудить любые прослушивающие потоки.

Часто мы хотим, чтобы наши классы были потокобезопасными, поэтому мы помещаем блокировки в наш код. Однако часто бывает, что наш класс будет использоваться только одним потоком. Это означает, что блокировки бесполезно замедляют наш код ... именно здесь умная оптимизация в среде CLR может повысить производительность.

Я не уверен в реализации блокировок Microsoft, но в DotGNU и Mono флаг состояния блокировки хранится в заголовке каждого объекта. Каждый объект в .NET (и Java) может стать блокировкой, поэтому каждый объект должен поддерживать это в своем заголовке. В реализации DotGNU существует флаг, который позволяет использовать глобальную хэш-таблицу для каждого объекта, который используется как блокировка, - это имеет преимущество устранения 4-байтных служебных данных для каждого объекта. Это не очень удобно для памяти (особенно для встроенных систем, которые не сильно загружены в потоки), но они поражают производительность.

Оба Mono и DotGNU эффективно использовать семафоры для выполнения блокировки/в режиме ожидания, но использовать стиль SpinLock compare-and-exchange операции, чтобы устранить необходимость фактически выполнить жесткие блокировки, если это действительно необходимо:

Вы можете увидеть пример того, как мониторы могут реализовать здесь:

http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup