C ++共享

boost::shared_mutexstd::shared_mutex (C ++ 17)可用于单个写入器,多个读取器访问。 作为一个教育练习,我将一个使用spinlocking的简单实现放在一起,并具有其他限制(例如公平性策略),但显然不打算用于实际应用。

这个想法是,如果没有线程持有锁,互斥量会保持为零的引用计数。 如果> 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);
    }
};

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

lock_shared肯定看起来不错:在读取时旋转,只有在性能比在cmpxchg上旋转更好时才尝试cmpxchg。 尽管我认为你是为了正确而被迫进入的,为了避免将-1改为0并解锁写锁。

我认为unlock_shared应该使用mo_release ,而不是mo_relaxed ,因为它需要从共享数据结构中排序负载,以确保写入器在读取器临界区发生负载之前不会开始写入。 (即使x86仅执行StoreLoad重新排序,LoadStore重新排序也是在弱有序体系结构上的事情。)Release操作将命令先前的负载并将它们保存在临界区域内。


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

不,您仍需要将写入保留在关键部分内,因此cmpxchg仍需要与unlock_shared同步(使用C ++术语)释放存储。

链接地址: http://www.djcxy.com/p/37605.html

上一篇: C++ shared

下一篇: Bootstrap or Material Design Lite or Both