如何使用C ++ 11原子库实现seqlock锁

Har*_*ong 5 c++ atomic c++11

我想使用c ++ 11原子库编写一个seqlock。我已阅读有关stackoverflow上seqlock的一些问题?但是没人帮我 我使用的算法很常见,您可以在任何地方找到它,这是我的代码:

struct sequence_spinlock_t {

    void write_lock() {
        lock.lock();
        flags.fetch_add(1, memory_order_acquire); //A

    }

    void write_unlock() {
        flags.fetch_add(1, memory_order_release); //B
        lock.unlock();
    }

    void read_enter(uintptr_t *flag) {
        for (;;) {
            uintptr_t f = flags.load(memory_order_acquire); //C
            if ((f & 1) == 0) {
                *flag = f;
                break;
            }
            pause();
        }
    }

    bool_ read_leave(uintptr_t flag) {                                        

        uintptr_t f = flags.load(memory_order_relaxed); //D
        return f == flag;
    }

    spinlock_t lock;
    atomic_uintptr_t flags;
};

    //read thread
    uintptr_t flag;
    do {
        lock.read_enter(&flag);      (0)
        //read something             (1)
    } while(!lock.read_leave(flag))  (2)


    //write thread
    lock.write_lock();              (3)
    //write something               (4)
    lock.write_unlock();            (5)
Run Code Online (Sandbox Code Playgroud)

我确保我在B和C处正确使用了memory_order标记。

我认为这在A和D都是不正确的。

想想我们同时读取和写入受保护的数据,我担心D处的标志的读取值太旧了,我们不读取write_lock()写入的最新值,但是我们读取受保护的数据的最新值由写线程编写(在x86系统上可能不会发生,但是我不认为代码在x86上运行。)在读取线程完成读取受保护的数据之后,由于标志的读取值太旧,找不到序列已增加。从循环读取线程产量,我们犯了一个错误。

(1)中受保护数据的读取值写入(4),(2)中标志的读取值未写入(3)(这是我们上次解锁写锁时写入的值)。为什么我认为有错误。

但是我真的不知道要解决此问题。我试图在read_leavee()和write_locke()之间建立“同步”关系(我希望“ read_leave()与write_locke()同步”)。但是没有存储read_leave()中的操作,所以我失败了。

(哦!我很难理解c ++标准规范,部分原因是我不是来自英语国家。)

jan*_*neb 2

在 read_leave 中使用 memory_order_relaxed 本身是可以的,但您确实需要确保在加载标志变量之前已加载数据值。您可以使用 std::atomic_thread_fence 来完成此操作。即你的 read_leave 应该看起来像

布尔 read_leave(uintptr_t 标志){ atomic_thread_fence(内存_顺序_获取); uintptr_t f = flag.load(memory_order_relaxed); 返回 f == 标志; }

FWIW,通过此更改,您的代码看起来大致类似于http://safari.ece.cmu.edu/MSPC2012/slides_posters/boehm-slides.pdf中的示例 3