我在 <sys/atomic.h> 中使用 SunOs 提供的原子操作,
void *atomic_cas_ptr(volatile void *target, void *cmp, void *newval);
现在要使其可用,我必须检查此函数返回的旧值和被调用函数 cmp 传递的旧值是否相同,如果它们是则操作是成功的。
但我有一定的疑问:由于这个函数返回一个指向旧值的 void 指针,我们称它为 void *old 并且我正在传递 void *cmp,那么我需要比较这两个 old 和 cmp,所以我将如何比较这两个 ?如果在比较 *old 时发生了变化,那我该怎么办?
本质上,我想做的是在另一个函数内部扭曲这个函数,该函数接受这三个参数并返回真或假,这表明成功或失败。
有关CAS
,我读到将其称为无锁操作是用词不当,因为它最终会锁定硬件(锁定总线),对吗?这就是为什么 CAS 操作成本高的原因。
1 回答
可能函数声明让你感到困惑。这个函数不返回一个指向旧值(什么?)的指针,而是返回指向的内存中的旧值target
(它实际上应该是一个指向 void* 的指针,即void* volatile * target
)。
通常,如果 CAS 原语返回旧值而不是布尔值,您可以通过以下方式检查 CAS 是否成功:
void* atomic_ptr; // global atomically modified pointer
void* oldval, newval, comparand; // local variables
/* ... */
oldval = atomic_cas_ptr( (void*)&atomic_ptr, /* note that address is taken */
comparand, newval );
if( oldval == comparand ) {
// success
} else {
// failure
}
因此,当您比较 old_val 和 comparand 时,您将使用不会同时更改的局部变量(而全局 atomic_ptr 可能会再次更改),并且您比较指针值而不取消引用。
你想要的功能应该是这样的:
bool my_atomic_cas_ptr(volatile void* target, void* comparand, void* newval)
{
return (comparand == atomic_cas_ptr(target, comparand, newval));
}
请注意,由于在某些算法中应该知道旧值(CAS 之前的那个),因此最好让 CAS 原语返回旧值而不是布尔值,因为您可以轻松地从前者构建后者,而相反的则更多复杂且低效(请参阅以下代码,该代码试图从返回布尔值的 MacOS CAS 原语中获取正确的旧值)。
void* CAS(void* volatile* target, void* comparand, void* newval)
{
while( !OSAtomicCompareAndSwapPtr(comparand, newval, target) ) {
void* snapshot = *target;
if( snapshot!=comparand ) return snapshot;
}
return comparand;
}
至于 CAS 锁定内存总线,它取决于硬件。旧的 x86 处理器确实如此,但在现代 x86 系统中却不同。一是没有中央公交;它被 AMD 的 HyperTransport 和英特尔的 QuickPath 互连所取代。其次,在最近几代 CPU 中,锁定指令并非全部序列化(参见一些数据显示不同内存地址上的锁定指令不会干扰)。最后,在普遍接受的定义中,锁自由是系统范围内进展的保证,而不是缺少序列化同步。