Генераторы барьера памяти - PullRequest
24 голосов
/ 05 июля 2011

Чтение Учебник Джозефа Албахари по созданию потоков , в качестве генераторов барьеров памяти упоминаются следующие:

  • C # s lock оператор (Monitor.Enter / Monitor.Exit)
  • Все методы класса Interlocked
  • Асинхронные обратные вызовы, которые используют пул потоков - к ним относятся асинхронные делегаты, обратные вызовы APM и продолжения задач
  • Установка и ожидание на сигнальной конструкции
  • Все, что зависит от сигнализации, например запуск или ожидание задачи

Кроме того, Ханс Пассант и Брайан Гидеон добавили следующее (при условии, что ни одна из них не вписывается ни в одну из предыдущих категорий):

  • Запуск или пробуждение нити
  • Переключение контекста
  • Thread.Sleep()

Мне было интересно, был ли этот список завершен (можно ли вообще составить полный список)

РЕДАКТИРОВАТЬ предложенные дополнения:

  • Летучие (чтение подразумевает забор приобретения, написание подразумевает освобождение)

Ответы [ 3 ]

33 голосов
/ 03 августа 2011

Вот мой взгляд на эту тему и попытка представить квази-полный список в одном ответе.Если я столкнусь с любыми другими, я буду время от времени редактировать свой ответ.

Механизмы, которые, как правило, согласовывают, создают неявные барьеры:

  • Все Monitor методы класса, включаяКлючевое слово C # lock
  • Все Interlocked методы класса.
  • Все Volatile методы класса (.NET 4.5 +).
  • Большинство SpinLock методов, включая Enter и Exit.
  • Thread.Join
  • Thread.VolatileRead и Thread.VolatileWrite
  • Thread.MemoryBarrier
  • Ключевое слово volatile.
  • Все, что запускает поток или вызывает выполнение делегата в другом потоке, включая QueueUserWorkItem, Task.Factory.StartNew, Thread.Start, предоставленные компилятором BeginInvoke методы и т. Д.
  • Использованиемеханизм сигнализации, такой как ManualResetEvent, AutoResetEvent, CountdownEvent, Semaphore, Barrier и т. д.
  • Использование операций маршалинга, таких как Control.Invoke, Dispatcher.Invoke, SynchronizationContext.Post и т. д..

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

  • Thread.Sleep (предложенные мной и, возможно, другими в связи с тем, чтос помощью этого метода в коде, который обнаруживает проблему с барьером памяти)
  • Thread.Yield
  • Thread.SpinWait
  • Lazy<T> в зависимости от того, какое значение указано LazyThreadSafetyMode

Другие заметные упоминания:

  • Обработчики добавления и удаления по умолчанию для событий в C #, поскольку они используют lock или Interlocked.CompareExchange.
  • x86 хранилищаИмеют семантику ограничения выпуска
  • Реализация CLI в Microsoft имеет семантику ограничения выпуска при записи, несмотря на то, что спецификация ECMA не предписывает это.
  • MarshalByRefObject, кажется, подавляет определенные оптимизации в подклассах, которыеможет заставить его выглядеть так, как будто присутствует неявный барьер памяти.Спасибо Гансу Пассанту за то, что обнаружили это и обратили на это мое внимание. 1

1 Thisобъясняет, почему BackgroundWorker работает правильно, без volatile в базовом поле для свойства CancellationPending.

11 голосов
/ 05 июля 2011

Кажется, я вспоминаю, что реализации методов Thread.VolatileRead и Thread.VolatileWrite фактически вызывают полные заборы, а не половину заборов.

Это глубоко прискорбно, поскольку люди могли бы полагаться на это поведение неосознанно;возможно, они написали программу, которая требует полного забора, думают, что им нужен половинный забор, думают, что они получают половинный забор, и их ждет неприятный сюрприз, если реализация этих методов когда-либо даст половинный забор.

Я бы избегал этих методов.Конечно, я бы избегал всего, что связано с кодом с низкой блокировкой, не будучи достаточно умным, чтобы писать его правильно ни в чем, кроме самых тривиальных случаев.

3 голосов
/ 05 июля 2011

Ключевое слово volatile также действует как барьер памяти.Смотри http://blogs.msdn.com/b/brada/archive/2004/05/12/130935.aspx

...