CAS指令如何保证原子性

Posted

技术标签:

【中文标题】CAS指令如何保证原子性【英文标题】:How does CAS instructions guarantee atomicity 【发布时间】:2016-08-14 08:53:01 【问题描述】:

根据Wiki,CAS 做了这样的事情:

function cas(p : pointer to int, old : int, new : int) returns bool 
    if *p ≠ old 
        return false
    
    *p ← new
    return true

好吧,在我看来,如果多个处理器尝试使用相同的参数执行 CAS 指令,那么可能会同时进行多次写入尝试,因此无论如何这样做都不安全。

我哪里错了?

【问题讨论】:

同时进行多次写入不是问题,只要清楚谁赢了。您可能会想到ABA problem,它需要额外的处理才能安全。但对于整数来说,这没问题。 @Voo "同时进行多次写入不是问题" -- 你确定吗?我认为这样做是不安全的,因为例如,x86 不能保证非对齐 DWORD 的写入原子性 【参考方案1】:

同时来自多个内核的原子读-比较-写指令确实相互竞争,但这取决于硬件来解决。 Hardware arbitration of atomic RMW instructions 在现代 CPU 中是真实存在的,并且提供了一定程度的公平性,因此在 lock cmpxchg 上旋转的一个线程不能完全阻止其他线程做同样的事情。 (虽然这是一个糟糕的设计:最好在获取负载上旋转,并且只有在它应该成功时才执行 CAS)

无法保证它们发生的顺序,这就是为什么您需要仔细设计算法,以便正确性仅取决于比较和交换是原子的。 (ABA problem 是一个常见的陷阱)。


顺便说一句,整个伪代码块作为单个原子操作发生。对于硬件而言,将读取-比较-写入或读取-修改-写入作为单个原子操作发生比仅仅存储要困难得多,MESIF/MOESI 处理得很好。

你确定吗?我认为这样做是不安全的,因为例如,x86 不能保证非对齐 DWORD 的写入原子性

lock cmpxchg 使操作原子化,而不管对齐方式。对于未对齐的情况,它可能会慢很多,尤其是在以原子方式修改单个缓存行还不够的缓存行拆分上。

另请参阅Atomicity on x86,我在这里解释了原子操作的含义。

【讨论】:

只有CMPXCHG 指令还不够吗?我也应该用LOCK前缀标记它吗? @FrozenHeart:如果你希望它是原子的,你需要locklock 对于带有内存操作数的 xchg 是隐含的,但对于任何其他指令都不是。 所以,CAS 本身不足以提供原子性,所以我们也应该在它前面加上 LOCK 前缀。那么像CMPXCHG 这样的指令有什么意义呢?就因为我们当时只能LOCK一条指令? 使用诸如cmpxchg 等不带前缀的指令至少在以下几个场景中很有用:(1) 在单 CPU 系统上,可以删除锁定前缀,至少在晦涩的场景之外例如 DMA 或其他非 CPU 代理在同一个宏上竞争。单 CPU 构建(即 CONFIG_SMP=n)就是一个很好的例子 - 大多数原子操作都隐藏在宏后面,当 SMP 被禁用时,可以简单地省略 lock 前缀。如果没有 lock 前缀,面对中断,您仍然拥有指令原子性,这就是您所需要的。 @BeeOnRope:这很好。 Linux 实际上比这更进一步:在 UP 系统上引导 SMP 内核实际上会将 lock 前缀修补到所有 UP 不需要的地方的 0x90 NOP 中。如果禁用抢占,它还会修补一些锁定/解锁代码。 (IIRC,内核日志消息是patching SMP alternatives 或其他东西)。关于在指令级别上原子化的优点;我可以看到非锁定的 CMPXCHG 实际上是如何有用的,就像inc [foo] is different from separate load/store 一样。【参考方案2】:

如果您阅读 wiki,它会说 CAS“是您发布的代码的以下伪代码的原子版本”。原子意味着代码将在不受其他线程中断的情况下执行。因此,即使多个线程尝试使用相同的参数(如您所建议的那样)同时执行此代码,也只有其中一个会返回 true,因为实际上它们不会同时执行,因为原子性要求它们单独运行。

而且由于您提到“x86 不保证非对齐 DWORD 的写入原子性”,因此这里也不是问题,因为 cas 函数的原子属性。

【讨论】:

以上是关于CAS指令如何保证原子性的主要内容,如果未能解决你的问题,请参考以下文章

spark append 如何保证原子性

CAS怎么保证原子性的

CAS怎么保证原子性的

synchronized能够保证原子性吗

Java并发- synchronized与CAS

volatile关键字与内存可见性&原子变量与CAS算法