C++ 我是否可以使用cmpxchg16b以原子方式复制指向指针和int的指针,同时递增int(原子引用计数)?
我的目标是复制指针并以原子方式递增引用计数器,而不锁定。我可以使用cmpxchg16b吗,或者有一种更简单的方法?我对汇编程序一无所知,所以请原谅任何明显的错误 编辑:类似于,但适用于此英特尔体系结构。(我还在试着看看我是否能将dobbs博士文章中的内容应用到我的平台上。原子操作似乎做得有点不同) 示例存储器C++ 我是否可以使用cmpxchg16b以原子方式复制指向指针和int的指针,同时递增int(原子引用计数)?,c++,multithreading,assembly,concurrency,C++,Multithreading,Assembly,Concurrency,我的目标是复制指针并以原子方式递增引用计数器,而不锁定。我可以使用cmpxchg16b吗,或者有一种更简单的方法?我对汇编程序一无所知,所以请原谅任何明显的错误 编辑:类似于,但适用于此英特尔体系结构。(我还在试着看看我是否能将dobbs博士文章中的内容应用到我的平台上。原子操作似乎做得有点不同) 示例存储器 struct MyThing { void * pSomeObj; uint64_t counter; __attribute__((aligned(16))); }; 我想做的
struct MyThing {
void * pSomeObj;
uint64_t counter;
__attribute__((aligned(16)));
};
我想做的是这样的事情
MyThing* globalSource = ...;
...
MyThing* dest;
//copy the pointer while incrementing counter all in one shot. (to prevent data race where a copy is done and some other thread deletes the pointer before the the counter gets a chance to be updated)
AtomicCopyAndIncrement(dest, globalSource);
从逻辑上讲,这就是我想要实现的目标:
bool AtomicCopyAndIncrement(MyThing *dest, MyThing *src) {
//begin atomic
dest = src;
dest->counter++;
//end atomic
return true;
}
此函数只是按原样交换所有内容。我认为这是对的
bool AtomicSwap(MyThing *dest, MyThing *src)
{
bool swap_result;
__asm__ __volatile__
(
"lock cmpxchg16b %0;"
"setz %3;"
: "+m" (*dest), "+a" (dest->pSomeObj), "+d" (dest->counter), "=q" (swap_result)
: "b" (src->pSomeObj), "c" (src->counter)
: "cc", "memory"
);
return swap_result;
}
此函数应采用src并将其设置为等于dest,同时递增在dest中分配的计数器
bool AtomicCopyAndIncrement(MyThing *dest, MyThing *src)
{
bool swap_result;
__asm__ __volatile__
(
"lock cmpxchg16b %0;"
"setz %3;"
//all wrong
: "+m" (*dest), "+a" (dest->pSomeObj), "+d" (dest->counter), "=q" (swap_result)
: "b" (src->pSomeObj), "c" (src->counter +1)
: "cc", "memory"
);
return swap_result;
}
这会使计数器递减,但会将点复制回自身。我不知道这是否必要,但我认为这是正确的编辑我很确定这是错的
bool AtomicDecrement(MyThing *dest)
{
bool swap_result;
__asm__ __volatile__
(
"lock cmpxchg16b %0;"
"setz %3;"
: "+m" (*dest), "+a" (dest->pSomeObj), "+d" (dest->counter), "=q" (swap_result)
: "b" (dest->pSomeObj), "c" (dest->counter -1)
: "cc", "memory"
);
return swap_result;
}
使用场景如下所示:
线程0:
MyThing* newThing;
do {
if (newThing) delete newThing;
newThing = new MyThing;
newThing->pSomeObj->SomeUpdate();
}
while (AtomicSwap(newThing, globalThing));
//then deal with the memory in newThing
线程1-N:
MyThing *currentThing = new MyThing;
//make currentThing->pSomeObj the same as globalThing->pSomeObj, and increment the counter
AtomicCopyAndIncrement(currentThing, globalThing);
考虑使用
std::atomic
。如果目标体系结构提供它,它将编译成128位CAS,并且比内联汇编程序更干净、更易于使用、更独立于平台
下面是一个示例,您可以使用它来更新指针并以原子方式递增计数器:
std::atomic<MyThing>* target = ...;
MyThing oldValue = target->load();
MyThing newValue { newPointer, 0 };
do{
newValue.counter = oldValue.counter+1;
while(target->compare_exchange_strong(oldValue,newValue))
std::atomic*目标=。。。;
虚构oldValue=target->load();
虚构新值{newPointer,0};
做{
newValue.counter=oldValue.counter+1;
while(target->compare\u exchange\u strong(旧值、新值))
除非我看错了你的答案,否则我实际上不想做交换。我想做一个原子赋值和增量。Dobbs博士的文章基本上就是我想做的。我想也许我可以用cmpxchg16b做一些技巧来实现这一点。我修改了我的第一个AtomicCopyAndIncrement()列表更清楚。谢谢。@marathon:您在哪里看到交换?您只将newValue
分配给oldValue
。这里没有交换!好吧,也许我不明白。看起来您已经分配了一个目标,您正在更改目标。我想做的是预先分配(全局)一个源和未分配的目标,将指针复制到目标并递增。其他线程可以随时调出源,因此这就是为什么这必须是原子的。@marathon:只需首先将源以原子方式读取,方法是将源包装到一个原子
,然后使用加载
。然后,您将得到一个local变量不能被另一个线程污染。现在您可以将\u exchange
此变量与目标进行比较。当然,只有加载和交换是原子的,而不是整个过程。如果不锁定,您无法以原子方式完成整个过程:没有CPU可以处理两个不同的内存位置(源和目标)原子性。@Mave:不保证。只有在体系结构支持的情况下。您可以使用is\u lock\u free
方法检查是否在不需要锁的情况下执行原子上的操作。