shared_lock implemented with 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()在你的旋转循环中,如果多个线程都试图同时获得锁,可能会有一些backoff来减少争用。

通常您希望在看到锁可用之前将其旋转为只读,而不是一直使用原子rmw。(参见cmpxchg是否在失败时写入目标缓存行?)如果没有,是不是比xchg更好呢?

另外,short是一个奇怪的选择;如果任何大小的表现比int差,它通常是短的。但如果这能帮助它和你要修改的数据打包到同一条缓存行中,那就没问题了。(尽管该缓存行将成为其他线程试图获取锁的错误共享争用的受害者。)

最新更新