使用gcc原子内置函数的原子交换函数

Sta*_*ked 8 c++ atomic

这是通用原子交换函数的正确实现吗?我正在寻找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);
}
Run Code Online (Sandbox Code Playgroud)

如果没有,我该怎么做才能修复它?

另外:__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);
}
Run Code Online (Sandbox Code Playgroud)

PS:GNU C++中的原子交换是一个类似的问题,但它没有回答我的问题,因为提供的答案需要C++ 11,std::atomic并且它具有签名Data *swap_data(Data *new_data),对于swap函数而言似乎没有任何意义.(它实际上将提供的参数与在函数之前定义的全局变量交换.)

Jas*_*son 11

请记住,此版本的交换不是完全原子操作.虽然b将以原子方式复制到a的值,但是a可以将另一个修改的值复制到b另一个线程的值.换句话说b,对于其他线程,赋值不是原子的.因此,您最终可能会遇到这样一种情况,其中a == 1,b == 2在gcc内置之后,您最终得到了返回a == 2的值1,但是现在另一个线程已经更改了bto 的值3,并且您在b其中写入了该值的价值1.因此,虽然您可能在"技术上"交换了值,但您没有原子地执行...另一个线程触及b了gcc原子内置返回和返回值赋值之间的值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!!)
Run Code Online (Sandbox Code Playgroud)

说实话,你不能在没有硬件操作(如DCAS或弱负载链接/存储条件)的情况下进行两个独立内存位置的无锁原子交换,或者可能使用某些其他方法(如事务内存(本身)倾向于使用细粒度锁定).

其次,正如您的函数现在编写的那样,如果您希望您的原子操作同时具有获取和释放语义,那么是的,您将不得不放入__sync_lock_release,或者您将不得不添加一个完整的通过记忆障碍__sync_synchronize.否则它只会获得语义__sync_lock_test_and_set.尽管如此,它并没有原子地交换两个独立的内存位置......