Как правильно прочитать int-поле Interlocked.Increment? - PullRequest
54 голосов
/ 26 мая 2011

Предположим, у меня есть энергонезависимое поле int и поток, который Interlocked.Increment s его.Может ли другой поток безопасно прочитать это непосредственно, или чтение также должно быть заблокировано?

Ранее я думал, что мне нужно использовать блокированное чтение, чтобы гарантировать, что я вижу текущее значение, так как, в конце концов,поле не является изменчивымЯ использовал Interlocked.CompareExchange(int, 0, 0) для достижения этой цели.

Однако я наткнулся на этот ответ , который предполагает, что на самом деле обычное чтение всегда будет видеть текущую версию Interlocked.IncrementЗначение ed, и поскольку чтение int уже атомарно, нет необходимости делать что-то особенное.Я также нашел запрос, в котором Microsoft отклоняет запрос на Interlocked.Read (ref int) , еще раз предполагая, что это полностью избыточно.

Так что я могу действительно безопасно прочитать большинствотекущее значение такого поля int без Interlocked?

Ответы [ 4 ]

19 голосов
/ 26 мая 2011

Если вы хотите гарантировать, что другой поток будет читать последнее значение, вы должны использовать Thread.VolatileRead(). (*)

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

Есть несколько участников, которые могут изменять код: компилятор, JIT-компилятор и процессор. Неважно, какой из них показывает, что ваш код не работает. Единственная важная вещь - модель памяти .NET , так как она определяет правила, которым должны следовать все участники.

(*) Thread.VolatileRead() на самом деле не получает последнее значение. Это прочитает значение и добавит барьер памяти после чтения. Первое энергозависимое чтение может получить кэшированное значение, но второе получит обновленное значение, поскольку барьер памяти первого энергозависимого чтения вызвал обновление кэша, если это было необходимо. На практике эта деталь не имеет большого значения при написании кода.

13 голосов
/ 28 марта 2014

Немного мета проблемы, но хороший аспект использования Interlocked.CompareExchange(ref value, 0, 0) (игнорируя очевидный недостаток, который труднее понять при использовании для чтения), заключается в том, что он работает независимо от int или long.Это правда, что int чтения всегда являются атомарными, но long чтения не являются или могут не быть, в зависимости от архитектуры.К сожалению, Interlocked.Read(ref value) работает, только если value имеет тип long.

Рассмотрим случай, когда вы начинаете с поля int, что делает невозможным использование Interlocked.Read(), поэтому вместо этого вы будете читать значение напрямую, поскольку оно в любом случае атомарно.Однако позже в процессе разработки вы или кто-то еще решите, что требуется long - компилятор не предупредит вас, но теперь у вас может быть небольшая ошибка: доступ на чтение больше не гарантированно будет атомарным.Я нашел использование Interlocked.CompareExchange() лучшей альтернативой здесь;Это может быть медленнее, в зависимости от базовых инструкций процессора, но это безопаснее в долгосрочной перспективе.Я не знаю достаточно о внутренностях Thread.VolatileRead(), хотя;Это может быть «лучше» в отношении этого варианта использования, поскольку он предоставляет еще больше сигнатур.

Я бы не пытался читать значение напрямую (т.е. без какого-либо из вышеперечисленных механизмов) в цикле или при любом жестком вызове методапоскольку даже если записи являются энергозависимыми и / или имеют барьер памяти, ничего не говорит компилятору о том, что значение поля может фактически измениться между двумя чтениями .Таким образом, поле должно быть либо volatile, либо должна использоваться любая из данных конструкций.

Мои два цента.

8 голосов
/ 26 мая 2011

Вы правы, что вам не нужна специальная инструкция для атомарного чтения 32-битного целого числа, однако это означает, что вы получите «целое» значение (т.е. вы не получите часть одной записии часть другого ).У вас нет никаких гарантий, что значение не изменится после прочтения.

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


В двух словах, атомарность обеспечивает выполнение операции полностью и неделимо.Учитывая некоторую операцию A, которая содержала N шагов, если вы сделали это в операции сразу после A, вы можете быть уверены, что все N шаги произошли в изоляции от одновременных операций.

Если выбыло два потока, которые выполнили атомарную операцию A, вы гарантированно увидите только результат complete для одного из двух потоков .Если вы хотите координировать потоки, атомарные операции могут быть использованы для создания необходимой синхронизации.Но атомарные операции сами по себе не обеспечивают синхронизацию более высокого уровня.Семейство методов Interlocked стало доступным для обеспечения некоторых фундаментальных атомарных операций.

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

Хотя это не относится к вашей проблеме сразу, чтение ACID (атомарность, согласованность, изоляция и долговечность) в отношении баз данных может помочь вамс терминологией.

0 голосов
/ 26 мая 2011

Да, все, что вы прочитали, правильно.Interlocked.Increment разработан таким образом, что нормальные чтения не будут ложными при внесении изменений в полеЧтение поля не опасно, написание поля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...