从GCC原子操作构建的轻量级自旋锁?

Tho*_*mas 11 c++ multithreading gcc pthreads thread-safety

我希望在我的项目中尽可能减少同步并编写无锁代码.当绝对必要时,我喜欢用pthread和win32互斥锁替换原子操作中构建的轻量级自旋锁.我的理解是这些是下面的系统调用并且可能导致上下文切换(对于非常快速的关键部分而言,这可能是不必要的,其中简单地旋转几次将是更可取的).

我所指的原子操作在这里有很好的记录:http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Atomic-Builtins.html

这是一个例子来说明我在说什么.想象一下可能有多个读者和作者的RB树.RBTree :: exists()是只读且线程安全的,RBTree :: insert()需要单个编写器(而不是读者)的独占访问才是安全的.一些代码:

class IntSetTest
{
private:
    unsigned short lock;
    RBTree<int>* myset;

public:
    // ...

    void add_number(int n)
    {
        // Aquire once locked==false (atomic)
        while (__sync_bool_compare_and_swap(&lock, 0, 0xffff) == false);

        // Perform a thread-unsafe operation on the set
        myset->insert(n);

        // Unlock (atomic)
        __sync_bool_compare_and_swap(&lock, 0xffff, 0);
    }

    bool check_number(int n)
    {
        // Increment once the lock is below 0xffff
        u16 savedlock = lock;
        while (savedlock == 0xffff || __sync_bool_compare_and_swap(&lock, savedlock, savedlock+1) == false)
            savedlock = lock;

        // Perform read-only operation    
        bool exists = tree->exists(n);

        // Decrement
        savedlock = lock;
        while (__sync_bool_compare_and_swap(&lock, savedlock, savedlock-1) == false)
            savedlock = lock;

        return exists;
    }
};
Run Code Online (Sandbox Code Playgroud)

(我们假设它不必是例外安全的)

这段代码确实是线程安全的吗?这个想法有利有弊吗?有什么建议?如果线程不是真正并发的话,使用像这样的自旋锁是个坏主意吗?

提前致谢.;)

Aid*_*lly 4

你需要一个volatile限定符lock,我也会把它变成一个sig_atomic_t。如果没有volatile限定符,此代码:

    u16 savedlock = lock;
    while (savedlock == 0xffff || __sync_bool_compare_and_swap(&lock, savedlock, savedlock+1) == false)
        savedlock = lock;
Run Code Online (Sandbox Code Playgroud)

在 while 循环体中lock更新时可能不会重新读取。考虑0xffff 的savedlock情况。lock然后,savedlock在检查循环条件之前将为 0xffff,因此while条件将在调用之前短路__sync_bool_compare_and_swap。由于__sync_bool_compare_and_swap没有被调用,编译器不会遇到内存障碍,因此它可能合理地假设 的值lock在您下面没有改变,并避免在savedlock.

回复: ,这里sig_atomic_t有一个不错的讨论。适用于信号处理程序的相同注意事项也适用于线程。

通过这些更改,我猜您的代码将是线程安全的。不过,我仍然建议使用互斥体,因为您确实不知道在一般情况下您的 RB 树插入需要多长时间(根据我之前在该问题下的评论)。