用 std::atomic 实现的 shared_lock

Posted

技术标签:

【中文标题】用 std::atomic 实现的 shared_lock【英文标题】:shared_lock implemented with std::atomic 【发布时间】:2021-01-21 11:23:44 【问题描述】:

由于它的重量,我不能持有 shared_lock。相反,我实现了我认为是 shared_lock 但具有原子价值的东西。这段代码会起作用吗,还是我错过了什么?

更新:我在原子互斥体周围添加了 RAII 类。

namespace atm 
static const unsigned int g_unlocked = 0;
static const unsigned int g_exclusive = std::numeric_limits<unsigned int>::max();
using mutex_type = std::atomic<unsigned int>;

class exclusive_lock 
  public:
    exclusive_lock(mutex_type& mutex) : _mutex(mutex) 
      unsigned int expected = g_unlocked;
      while (!_mutex.compare_exchange_weak(expected, g_exclusive)) 
        _mm_pause();
        expected = g_unlocked;
      
    
    ~exclusive_lock()  _mutex.store(g_unlocked, std::memory_order_release); 
  private:
    mutex_type& _mutex;
;

class shared_lock 
  public:
    shared_lock(mutex_type& mutex) : _mutex(mutex) 
      unsigned int expected = _mutex;
      while (expected == g_exclusive || !_mutex.compare_exchange_weak(expected, expected + 1)) 
        _mm_pause();
        expected = _mutex;
      
    
    ~shared_lock()   _mutex.fetch_sub(1, std::memory_order_release); 
  private:
    mutex_type& _mutex;
;
 // namespace atm

【问题讨论】:

【参考方案1】:

为了正确起见,我认为这看起来很合理,我认为没有问题。我可能遗漏了一些东西,但是 seq_cst CAS 足以获得锁。看起来您通过使用最大值作为特殊的东西来避免整数环绕。

mutex=0解锁只需要release,而不是seq_cst。 (-=1 共享解锁相同,但这不会使其在 x86 上更有效,仅在弱排序 ISA 上)。另外,compare_exchange_weak 完全可以;无论如何,您都在循环重试,因此虚假失败与失败的比较没有什么不同。

如果您使用的是 x86,您通常希望在自旋循环中使用 _mm_pause(),并且如果多个线程都试图一次获取锁,则可能需要某种退避来减少争用。

并且通常您希望以只读方式旋转,直到您看到可用的锁,而不是继续使用原子 RMW。 (见Does cmpxchg write destination cache line on failure? If not, is it better than xchg for spinlock?)。

另外,short 是一个奇怪的选择;如果任何大小的性能比 int 差,它通常很短。但可能没问题,好吧,我想这是否有助于将它打包到与您正在修改的数据相同的缓存行中。 (尽管该缓存行将成为其他线程试图获取锁的虚假共享争用的受害者。)

【讨论】:

您给出了详细的答复,谢谢。不幸的是,我对原子知之甚少。能否请您解释或举例说明release 而非seq_cst 的含义。它与“存储/获取/加载”有关吗?我也不清楚“在看到可用的锁之前只读”的部分,因为我不得不花几个晚上从提供的链接中研究汇编程序和术语:) @vamirio-chan:我的意思是memory_order_release,比如mutex.store(0, std::memory_order_release);

以上是关于用 std::atomic 实现的 shared_lock的主要内容,如果未能解决你的问题,请参考以下文章

C++并发编程----实现线无锁线程安全的数据结构(《C++ Concurrency in Action》 读书笔记)

C++并发与多线程 11_std::atomic叙谈std::launch(std::async) 深入

std::atomic 和 std::condition_variable 等待、notify_* 方法之间的区别

原子读取然后用 std::atomic 写入

std::atomic_ref 如何为非原子类型实现?

如何用 std::atomic 实现无锁计数器?