【发布时间】:2018-05-10 08:47:03
【问题描述】:
我正在阅读[lock cmpxchg描述])https://www.felixcloutier.com/x86/CMPXCHG.html):
该指令可以与
LOCK前缀一起使用以允许 要以原子方式执行的指令。为了简化 到处理器总线的接口,目标操作数 接收一个写周期而不考虑的结果 比较。如果目标操作数被写回 比较失败;否则,源操作数是 写入目的地。 (处理器从不 产生一个锁定的读取,而不产生一个锁定的 写。)
现在考虑两个线程执行lock cmpxchg:
Thread 1 Thread 2
mov ebx, 0x4000 mov ebx, 0x4000 ; address
mov edx, 0x62ab6 mov edx, 0x62ab8 ; new val
mov eax, 0x62ab1 mov eax, 0x62ab1 ; old
lock cmpxchg [ebx], eax lock cmpxchg [ebx], eax ; <----- here
问题是线程 1 和线程 2 中的锁定 cmpxchg 会失败吗?
自从
目标操作数 接收一个写周期而不考虑的结果 比较
我可以猜测这两个线程都可以具有写入周期,并且由于与陈旧值进行比较,它们都可以恢复......但我不确定这是否正确。
也许我需要看一下cas实现细节,但是intel指令参考中没有指定(至少我找不到)
【问题讨论】:
-
问题出在“旧值”上。加载 eax 后,内存中的值可能会在开始比较之前再次更改。
-
@BoPersson 你能扩展一下吗?又被谁改了?好的,假设两个线程都将旧值加载到不同内核上的
eax中。内核开始执行lock cmpxchg之后接下来会发生什么。 -
“又被谁更改了?” - 由另一个线程或任何人(可能有更多的内核摆弄内存)。
lock将确保其中一个核心“获胜”并首先获得对该指令的访问权限。另一个必须等待(然后会看到修改后的值)。 -
@BoPersson 好的,明白了,谢谢。因此,使用
lock前缀无论如何都会有一个线程获胜。我对LL/SCpair 可能会虚假失败并且没有文档说 cas 不能这样的事实感到困惑。顺便说一句,你能推荐一些关于英特尔 CPU 上的CAS实现细节的阅读吗? -
您的示例实际上并没有说您知道旧值是
0x62ab1。也许这样说,或者更好地将其更改为mov eax, [ebx]以实际加载旧值。并且还断言没有第三个线程可以改变它。
标签: assembly concurrency x86-64 compare-and-swap