用 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) 深入