uint64_t n; // two 32-bit integers
return ( (uint32_t)(n >> 32) == (uint32_t)n );
如何以原子方式比较uint64_t的32个最高有效位和32个最低有效位中的值?
我认为一种可怕的解决方案是:获取自旋锁,读取32位最低有效位,读取32位最高有效位,进行比较并获得结果,释放自旋锁,返回结果。是否有一种不需要获取自旋锁就可以完成的方法?
CMPXCHG (int*)&n, (((int*)&n)+1)
(注意 - 这实际上是行不通的)。编辑:将语法更改为更接近实际x86语法。正如Serge所指出的,对于大多数汇编程序来说,在一个汇编指令中使用两个内存地址是不被支持的,因此这种方式不能直接从内存中工作。这意味着这种方法不能用于以原子方式比较64位变量的两个32位部分。一些汇编语言(至少PowerPC)能够提供特殊的指令(对于PowerPC,LWARX和STWCX),可以以多线程安全的方式使其工作,但这并不完全符合OP的要求,也不能在x86上工作。+1
而不是 +sizeof(int)
吗? - MarlonCMPXCHG (int*)&n, (((int*)&n)+1), res
是什么意思。有x86指令CMPXCHG,但它只有2个操作数(并且在EAX中有隐含的操作数-要与之比较的数字),而单独这个指令无法实现所请求的操作(显然需要多个指令来锁定)。 - Serge Dundich除非您还可以确保写入它们始终是原子的,否则整个操作(内存中两个值的原子比较)是没有意义的。它还受到固有竞争条件的影响;在您确定它们相等之时,它们可能已经改变,反之亦然。无论您试图解决什么问题,几乎肯定需要使用锁,而不是原子操作。
如果您的平台可以原子地检索64位数字,则可以在不使用锁的情况下完成。 如果可能,首先以您喜欢的方式(例如,在64位Windows上进行InterlockedOr64(ptr,0),如果您有32位x86 CPU,则没有办法 - 除非您拥有不早于Pentium的英特尔CPU并确保您的64位值为64位对齐,不确定其他供应商的x86 CPU),然后执行与检索到的值进行比较。
显然,您无法以可移植的方式完成此操作。 在无法原子地获取64位数字的平台上,无法在不使用锁的情况下完成此操作。
编辑
由于某些极具误导性的想法在这个讨论中获得了严重的流行,我觉得我的职责是写一些关于未能使用32位数字的Compare&Exchange来解决问题的注意事项。
假设我们有x86平台,那么我们可以编写asm代码:
mov eax, [num+4]
lock cmpxchg [num], eax
jz equal_case_code
; non-equal case code follows
equal_case_code:
; equal case code follows
显然,这个实现不是原子的 - 线程可能会在mov
和cmpxchg
指令之间中断(因为一条指令中不允许两个内存操作数)。
来自不同API的基于32位的Compare&Exchange函数(例如Win32 API的InterlockedCompareExchange)也无法提供正确的解决方案,因为它们的语义只允许对一个32位内存地址进行原子访问。
fild
已经是原子的了(没有锁前缀)。在古老的CPU上,FPU是外部的,可能不是这样,但在任何现代设备上都应该是这样的。 - R.. GitHub STOP HELPING ICE使用联合体怎么样?就像这样:
typedef union {
uint32_t small[2];
uint64_t full;
} bigint_t;
然后你去做:
uint64_t n; // two 32-bit integers
bigint_t mybigint;
mybigint.full = n;
return mybigint.small[0] == mybigint.small[1];
我不知道这是否是最快的方法,但如果你不将 uint64_t 复制到联合体中,而直接使用该联合体,它应该会相当快,因为对于比较操作来说它并不需要做任何额外的操作。
使用内联汇编将64位整数加载到MMX或SSE寄存器中(64位读取是原子性的),然后比较两半。