该指令可以与
LOCK
前缀一起使用,以允许指令以原子方式执行。
我的问题是
CMPXCHG
可以使用内存地址吗?从文档看来似乎没有,但任何人都可以确认只能在寄存器中使用实际的VALUE,而不是内存地址吗?
如果CMPXCHG
不是原子级且高级语言级CAS必须通过LOCK CMPXCHG
(带LOCK
前缀)来实现,那么引入这样的内容的目的是什么?完全没有指导?
答案 0 :(得分:18)
这似乎是你真正要求的一部分:
为什么
//register retryWhen Observable.retryWhen(new RetryWhenException()); //retry code. public class RetryWhenException implements Function<Observable<? extends Throwable>, Observable<?>>{ public Observable<?> apply(final Observable<? extends Throwable> observable) throws Exception { return observable.zipWith(Observable.range(1, count + 1), new BiFunction<Throwable, Integer, Wrapper>() { @Override public Wrapper apply(Throwable throwable, Integer integer) throws Exception { return new Wrapper(throwable, integer); } }).flatMap(new Function<Wrapper, Observable<?>>() { @Override public Observable<?> apply(Wrapper wrapper) throws Exception { long delay = 60 * 1000; //How can I add some code here to cancel this time and execute this api call immediately if I receive a event like network gets back? return Observable.timer(delay, TimeUnit.MILLISECONDS); } }); } }
含有内存操作数的lock
隐含cmpxchg
前缀like it is forxchg
?
简单的答案(其他人已经给出)只是英特尔以这种方式设计的。但这导致了一个问题:
为什么英特尔这样做?是否有
cmpxchg
没有lock
的用例?
在单CPU系统上,cmpxchg
相对于其他线程或在同一CPU核心上运行的任何其他代码是原子的。 (但不是&#34;系统&#34;观察者,如内存映射的I / O设备,或正常内存的DMA读取设备,因此即使在单处理器CPU设计上lock cmpxchg
也是相关的。)
上下文切换只能发生在中断上,中断发生在指令之前或之后,而不是在中间。在同一CPU上运行的任何代码都会将cmpxchg
视为完全执行或根本不执行。
例如,Linux内核通常使用SMP支持进行编译,因此它使用lock cmpxchg
作为原子CAS。但是,当在单处理器系统上启动时,它会将lock
前缀修补到nop
代码内联的所有代码,因为nop
cmpxchg
的运行速度比{{lock cmpxchg
快得多1}}。有关详细信息,请参阅此LWN article about Linux's "SMP alternatives" system。在热插拔第二个CPU之前,它甚至可以修补回lock
前缀。
详细了解单处理器系统in this answer上的单个指令的原子性,以及num++
上的int num
lock cmpxchg
的原子性cmpxchg8b
。有关像cmpxchg16b
这样的读取 - 修改 - 写入指令实现原子性如何工作的许多详细信息,请参阅@supercat's answer + comments。
(同样的推理也适用于xadd
/ add [mem], reg
和lock add [mem], reg
,它们通常只用于同步/原子操作,而不是使单线程代码运行得更快。显然memory-destination s <- c("コハニー", "Cat", "Blue", "زبان","D-1")
在"ABC" "Cat" "Blue" "ABC" "D-1"
情况之外很有用。)
答案 1 :(得分:11)
您正在将高级锁与恰好命名为LOCK
的低级CPU功能混合在一起。
无锁算法试图避免的高级锁可以保护执行可能需要任意时间的任意代码片段,因此,这些锁必须将线程置于等待状态,直到锁可用,这是一项代价高昂的操作,例如意味着要维护一个等待线程的队列。
这与CPU LOCK
前缀功能完全不同,它仅保护单个指令,因此可能仅在该单个指令的持续时间内保留其他线程。由于这是由CPU本身实现的,因此不需要额外的软件工作。
因此,开发无锁算法的挑战并不是完全消除同步,它可以归结为将代码的关键部分减少到由CPU本身提供的单个原子操作。
答案 2 :(得分:3)
LOCK前缀用于锁定当前命令的内存访问,以便CPU管道中的其他命令可以在此时访问内存。使用LOCK前缀,由于对同时执行的其他命令的存储器访问,命令的执行不会被CPU管道中的另一个命令中断。 INTEL手册说:
LOCK前缀只能添加到以下结构中 并且仅限于目的地的那些形式的指令 操作数是一个内存操作数:ADD,ADC,AND,BTC,BTR,BTS,CMPXCHG, CMPXCH8B,CMPXCHG16B,DEC,INC,NEG,NOT,OR,SBB,SUB,XOR,XADD和 XCHG。如果LOCK前缀与这些指令之一一起使用 源操作数是内存操作数,是未定义的操作码异常 (#UD)可能会生成。