如何解决shared_lock 用 std::atomic 实现
由于它的重量,我无法承受 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
解决方法
为了正确起见,我认为这看起来很合理,我看不出有什么问题。我可能遗漏了一些东西,但是 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 差,则它通常很短。但可能没问题,好吧,我想这是否有助于将它打包到与您正在修改的数据相同的缓存行中。 (尽管该缓存行将成为其他线程试图获取锁的虚假共享争用的受害者。)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。