这是通用原子交换功能的正确实现吗?我正在寻找GCC上与C ++ 03兼容的解决方案。
template<typename T>
void atomic_swap(T & a, T & b) {
static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded.");
T * ptr = &a;
b =__sync_lock_test_and_set(ptr, b);
__sync_lock_release(&ptr);
}
如果没有,我该怎么做才能解决它?
另外:__sync_lock_release
总是必要的吗?在搜索其他代码库时,我发现通常不会调用它。没有发布调用,我的代码如下所示:
template<typename T>
void atomic_swap(T & a, T & b) {
static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded.");
b = __sync_lock_test_and_set(&a, b);
}
PS:Atomic swap in GNU C++是一个类似的问题,但它没有回答我的问题,因为提供的答案需要C ++ 11的std::atomic
而且它的签名Data *swap_data(Data *new_data)
似乎不一样对swap
函数有意义。 (它实际上将提供的参数与在函数之前定义的全局变量交换。)
答案 0 :(得分:11)
请记住,此版本的swap不是完全原子操作。虽然b
的值将以原子方式复制到a
,但a
的值可能会被另一个线程复制到b
的另一个修改。换句话说,对b
的赋值与其他线程不是原子的。因此,您可能会遇到a == 1
和b == 2
的情况,并且在内置gcc之后,最终会返回a == 2
并返回1
的值,但现在另一个线程已将b
的值更改为3
,并在b
中使用1
的值覆盖该值。因此,虽然你可能在“技术上”交换了这些值,但是你并没有原子地做到这一点......另一个线程触及了来自gcc原子内置函数的返回之间b
的值,以及将值返回b
。从装配的角度来看,你有以下几点:
lea RAX, qword ptr [RDI] // T * ptr = &a;
mov RCX, qword ptr [RSI] // copy out the value referenced by b into a register
xchg [RAX], RCX // __sync_lock_test_and_set(&a, b)
mov qword ptr [RSI], RCX // place the exchange value back into b (not atomic!!)
老实说,如果没有DCAS或弱负载链接/存储条件等硬件操作,或者可能使用某些其他方法(如事务内存),则无法进行两个独立内存位置的无锁原子交换(它本身倾向于使用细粒度锁定。)
其次,正如您的函数现在被编写,如果您希望您的原子操作同时具有获取和释放语义,那么是的,您将不得不放置在__sync_lock_release
中,或者您必须通过__sync_synchronize
添加完整的内存屏障。否则它只会在__sync_lock_test_and_set
上获得语义。尽管如此,它并没有原子地交换两个独立的内存位置......