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

时间:2012-01-25 13:12:03

标签: 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);
}

如果没有,我该怎么做才能解决它?

另外:__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函数有意义。 (它实际上将提供的参数与在函数之前定义的全局变量交换。)

1 个答案:

答案 0 :(得分:11)

请记住,此版本的swap不是完全原子操作。虽然b的值将以原子方式复制到a,但a的值可能会被另一个线程复制到b的另一个修改。换句话说,对b的赋值与其他线程不是原子的。因此,您可能会遇到a == 1b == 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上获得语义。尽管如此,它并没有原子地交换两个独立的内存位置......

相关问题