Подтвердить что ты не робот

Генераторы барьеров памяти

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

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

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

  • Запуск или просыпание потока
  • Контекстный коммутатор
  • Thread.Sleep()

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

EDIT:

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

Ответ 1

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

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

  • Все методы класса Monitor, включая ключевое слово С# 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 указан

Другие примечательные упоминания:

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

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

Ответ 2

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

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

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