Генераторы барьера памяти
Читая учебник Джозефа Албахари о потоках, в качестве генераторов барьеров памяти упоминаются следующие:
- C# 's
lock
заявление (Monitor.Enter
/Monitor.Exit
) - Все методы на
Interlocked
учебный класс - Асинхронные обратные вызовы, которые используют пул потоков - к ним относятся асинхронные делегаты, обратные вызовы APM и продолжения задач
- Установка и ожидание на сигнальной конструкции
- Все, что зависит от сигнализации, например, запуск или ожидание задачи
Кроме того, Ханс Пассант и Брайан Гидеон добавили следующее (предполагая, что ни одна из них не вписывается ни в одну из предыдущих категорий):
- Запуск или пробуждение потока
- Переключение контекста
Thread.Sleep()
Мне было интересно, если этот список был завершен (если полный список можно было бы даже практически сделать)
РЕДАКТИРОВАТЬ дополнения предложил:
- Изменчивый (чтение подразумевает забор приобретения, написание подразумевает забор выпуска)
3 ответа
Вот мой взгляд на эту тему и попытка представить квази-полный список в одном ответе. Если я сталкиваюсь с другими, я буду время от времени редактировать свой ответ.
Механизмы, которые в целом согласованы для создания неявных барьеров:
- Все
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 Это объясняет почему BackgroundWorker
работает правильно, не имея volatile
на базовое поле для CancellationPending
имущество.
Кажется, я вспоминаю, что реализации методов Thread.VolatileRead и Thread.VolatileWrite фактически вызывают полные заборы, а не половину заборов.
Это глубоко прискорбно, так как люди могли бы полагаться на это поведение неосознанно; возможно, они написали программу, которая требует полного ограничения, думают, что им нужен половину ограничения, думают, что они получают половину ограничения, и их ждет неприятный сюрприз, если реализация этих методов когда-либо обеспечит половину ограничения.
Я бы избегал этих методов. Конечно, я бы избегал всего, что связано с кодом с низкой блокировкой, не будучи достаточно умным, чтобы писать его правильно во всем, кроме самых тривиальных случаев.
volatile
Ключевое слово действует как барьер памяти тоже. См. http://blogs.msdn.com/b/brada/archive/2004/05/12/130935.aspx