Interlocked.CompareExchange是否使用内存屏障?

18 c# optimization multithreading volatile memory-model

我正在阅读Joe Duffy关于Volatile读取和写入以及及时性的帖子,我正在尝试理解帖子中最后一个代码示例:

while (Interlocked.CompareExchange(ref m_state, 1, 0) != 0) ;
m_state = 0;
while (Interlocked.CompareExchange(ref m_state, 1, 0) != 0) ;
m_state = 0;
… 
Run Code Online (Sandbox Code Playgroud)

当执行第二CMPXCHG操作,它使用一个内存屏障,以保证价值m_state确实写入的最新值?或者它只是使用已存储在处理器缓存中的某些值?(假设m_state未声明为volatile).
如果我理解正确,如果CMPXCHG不会使用内存屏障,那么整个锁获取过程将不公平,因为第一个获取锁的线程很可能是将获得所有锁的线程.以下锁.我是否理解正确,或者我错过了什么?

编辑:主要问题实际上,在尝试读取m_state的值之前,调用CompareExchange是否会导致内存屏障.因此,当尝试再次调用CompareExchange时,是否所有线程都可以看到赋值0.

min*_*ang 23

任何具有锁定前缀的x86指令都具有完整的内存屏障.如Abel的回答所示,Interlocked*API和CompareExchanges使用锁定前缀等指令lock cmpxchg.所以,它意味着记忆围栏.

是的,Interlocked.CompareExchange使用内存屏障.

为什么?因为x86处理器就这样做了.从英特尔的第3A卷:系统编程指南第1部分,第7.1.2.2节:

对于P6系列处理器,锁定操作会序列化所有未完成的加载和存储操作(即等待它们完成).对于奔腾4和英特尔至强处理器,此规则也是如此,但有一个例外.引用弱有序内存类型(例如WC内存类型)的加载操作可能无法序列化.

volatile与此讨论无关.这是关于原子操作的; 为了支持CPU中的原子操作,x86保证完成所有先前的加载和存储.

  • @KindContributor:是的,在 ARMv8.1 之前,没有单指令原子比较交换。但 ARM / AArch64 也有完整的内存屏障指令,因此可能还需要“dmb ish”。MS的C++文档[`_InterlockedIncrement`内在](https://learn.microsoft.com/en-us/previous-versions/2ddez55b(v=vs.85))表明有`_acq`和`_rel`版本,大概暗示普通版本是 seq_cst。(因为没有 _relaxed 或 _sc)。 (2认同)

Mar*_*ell 10

ref不尊重通常的volatile规则,特别是在以下方面:

volatile bool myField;
...
RunMethod(ref myField);
...
void RunMethod(ref bool isDone) {
    while(!isDone) {} // silly example
}
Run Code Online (Sandbox Code Playgroud)

在这里,即使底层字段()是,RunMethod也不能保证发现外部变化; 不知道它,所以没有正确的代码.isDonemyFieldvolatileRunMethod

然而!这应该是一个非问题:

  • 如果您正在使用Interlocked,则Interlocked用于对该字段的所有访问
  • 如果您正在使用lock,则lock用于对该字段的所有访问

遵循这些规则,它应该工作正常.


重新编辑; 是的,这种行为是关键的一部分Interlocked.说实话,我不知道它是如何实现的(内存屏障等等 - 请注意它们是"InternalCall"方法,所以我无法检查;-p) - 但是:来自一个线程的更新将立即显示为所有其他人只要他们使用这些Interlocked方法(因此我的观点).


Abe*_*bel 6

似乎与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
Run Code Online (Sandbox Code Playgroud)

但在多处理器系统上,硬件锁用于防止其他内核同时访问数据:

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
Run Code Online (Sandbox Code Playgroud)

这里有一个有趣的阅读,并且有一些错误的结论,但是这个主题的优秀是这篇关于CompareExchange的博客文章.