Можно найти больше информации, о котором сервисах это влияет на документах .
Microsoft
Любая инструкция x86 с префиксом lock имеет полный барьер памяти . Как показано в ответе Абеля, API-интерфейсы Interlocked * и CompareExchanges используют инструкцию с префиксом lock , такую как lock cmpxchg
. Итак, это подразумевает забор памяти.
Да, Interlocked.CompareExchange использует барьер памяти.
Почему? Потому что это сделали процессоры x86. Из Intel , том 3A: Руководство по системному программированию, часть 1 , раздел 7.1.2.2:
Для процессоров семейства P6 заблокированные операции сериализуют все невыполненные операции загрузки и сохранения (то есть дождитесь их завершения). Это правило также справедливо для процессоров Pentium 4 и Intel Xeon, за одним исключением. Операции загрузки, которые ссылаются на слабо упорядоченные типы памяти (такие как тип памяти WC), не могут быть сериализованы.
volatile
не имеет ничего общего с этим обсуждением. Речь идет об атомарных операциях; для поддержки атомарных операций в ЦП x86 гарантирует выполнение всех предыдущих загрузок и сохранений.
ref
не соблюдает обычные правила volatile
, особенно в таких случаях, как:
volatile bool myField;
...
RunMethod(ref myField);
...
void RunMethod(ref bool isDone) {
while(!isDone) {} // silly example
}
Здесь RunMethod
не гарантирует выявить внешние изменения в isDone
, даже если базовое поле ( myField
) является изменчивым
; RunMethod
не знает об этом, поэтому у него неправильный код.
Однако! Это не должно вызывать проблем:
Interlocked
, тогда используйте Interlocked
для всего доступа к полю блокировку
, затем используйте блокировку
для весь доступ к полю Следуйте этим правилам, и все должно работать нормально.
Повторное редактирование; да, что поведение является важной частью Interlocked
. Честно говоря, я не знаю, как это реализовано (барьер памяти и т. Д. - обратите внимание, что это методы "InternalCall", поэтому я не могу проверить ;-p) - но да: обновления из одного потока будут немедленно видны для все остальные , пока используют методы Interlocked
(отсюда и моя точка зрения выше).
Связанные функции гарантированно остановят шину и процессор, пока он разрешает операнды. Непосредственным следствием этого является то, что ни один переключатель потоков на вашем или другом процессоре не прервет заблокированную функцию в середине ее выполнения.
Поскольку вы передаете ссылку на функцию C #, базовый код ассемблера будет работать с адрес фактического целого числа, поэтому доступ к переменной не будет оптимизирован. Он будет работать именно так, как ожидалось.
edit: Вот ссылка, которая лучше объясняет поведение инструкции asm: http://faydoc.tripod.com/cpu/cmpxchg.htm
Как вы можете видеть , шина останавливается из-за принудительного цикла записи, поэтому любые другие «потоки» (читай: другие ядра процессора), которые попытаются использовать шину одновременно, будут помещены в очередь ожидания.
MSDN говорит о функциях Win32 API: « Большинство взаимосвязанных функций обеспечивают полные барьеры памяти на всех платформах Windows »
(исключение составляют функции Interlocked с явной семантикой Acquire / Release)
Из этого я могу сделать вывод, что среда выполнения C # заблокирована дает те же гарантии, поскольку они задокументированы с идентичным во всем остальном поведением (и они разрешаются к внутренним операторам ЦП на платформах, которые я знаю). К сожалению, из-за тенденции MSDN размещать образцы вместо документации, это явно не прописано.
Кажется, есть некоторое сравнение с одноименными функциями Win32 API, но этот поток посвящен классу C # Interlocked
. Из самого его описания гарантируется, что его операции атомарны. Я не уверен, как это означает «полные барьеры памяти», как упоминалось в других ответах здесь, но судите сами.
В однопроцессорных системах ничего особенного не происходит, есть только одна инструкция:
FASTCALL_FUNC CompareExchangeUP,12
_ASSERT_ALIGNED_4_X86 ecx
mov eax, [esp+4] ; Comparand
cmpxchg [ecx], edx
retn 4 ; result in EAX
FASTCALL_ENDFUNC CompareExchangeUP
Но в многопроцессорных системах аппаратная блокировка используется для предотвращения одновременного доступа к данным другим ядрам:
FASTCALL_FUNC CompareExchangeMP,12
_ASSERT_ALIGNED_4_X86 ecx
mov eax, [esp+4] ; Comparand
lock cmpxchg [ecx], edx
retn 4 ; result in EAX
FASTCALL_ENDFUNC CompareExchangeMP
Интересное чтение с кое-где есть некоторые неправильные выводы, но в целом это сообщение блога на CompareExchange превосходно по этой теме.