C ++ shared_mutex实现

时间:2016-12-16 23:04:04

标签: c++ multithreading c++11 stdatomic

boost::shared_mutexstd::shared_mutex(C ++ 17)可用于单个写入者,多个读者访问。作为一项教育活动,我将一个使用自旋锁的简单实现放在一起,并有其他限制(例如公平政策),但显然不打算在实际应用中使用。

这个想法是,如果没有线程持有锁,则互斥锁保持引用计数为零。如果> 0,该值表示有权访问的读者数。如果为-1,则单个编写者可以访问。

这是一个没有数据竞争的正确实现(特别是使用的最小内存排序)吗?

#include <atomic>

class my_shared_mutex {
    std::atomic<int> refcount{0};
public:

    void lock() // write lock
    {
        int val;
        do {
            val = 0; // Can only take a write lock when refcount == 0

        } while (!refcount.compare_exchange_weak(val, -1, std::memory_order_acquire));
        // can memory_order_relaxed be used if only a single thread takes write locks ?
    }

    void unlock() // write unlock
    {
        refcount.store(0, std::memory_order_release);
    }

    void lock_shared() // read lock
    {
        int val;
        do {
            do {
                val = refcount.load(std::memory_order_relaxed);

            } while (val == -1); // spinning until the write lock is released

        } while (!refcount.compare_exchange_weak(val, val+1, std::memory_order_acquire));
    }

    void unlock_shared() // read unlock
    {
        refcount.fetch_sub(1, std::memory_order_relaxed);
    }
};

1 个答案:

答案 0 :(得分:3)

(我使用cmpxchg作为C ++ compare_exchange_weak函数的简写,而不是 cmpxchg instruction。)

lock_shared看起来确实很好:在读取时旋转并且只在尝试使用cmpxchg时,值的性能比在cmpxchg上旋转要好得多。虽然我认为你被迫进入正确性,但要避免将-1改为0并解锁写锁。

我认为unlock_shared应该使用mo_release,而不是mo_relaxed,因为它需要从共享数据结构中对订单进行排序,以确保作者不会开始写作在读者关键部分的负载发生之前。 (LoadStore reordering是弱有序体系结构上的一个东西,即使x86只进行StoreLoad重新排序。)A Release operation will order preceding loads and keep them inside the critical section

  

(在写lock中)://如果只有一个线程占用写锁,可以使用memory_order_relaxed吗?

不,您仍然需要将写入保留在关键部分内,因此cmpxchg仍需要与unlock_shared进行同步(在C ++术语中)发布存储。