在一些教程中,我看到了这样的自旋锁实现
class spin_lock
{
atomic<unsigned int> m_spin ;
public:
spin_lock(): m_spin(0) {}
~spin_lock() { assert( m_spin.load(memory_order_relaxed) == 0);}
void lock()
{
unsigned int nCur;
do { nCur = 0; }
while ( !m_spin.compare_exchange_weak( nCur, 1, memory_order_acquire ));
}
void unlock()
{
m_spin.store( 0, memory_order_release );
}
};
对于memory_order_acquire
和memory_order_release
操作,我们是否真的需要compare_exchange_weak
/ store
个标记?或者memory_order_relaxed
在这种情况下就足够了,因为没有Synchronizes-With关系?
更新:谢谢您的解释!我正在考虑没有使用它的上下文的spin_lock。
答案 0 :(得分:10)
需要memory_order_acquire
/ memory_order_release
。
您使用锁来保护多个线程之间的共享数据。如果您不在memory_order_release
方法中使用unlock
,则可以在unlock
方法之后重新排序对共享数据的任何写入。此外,如果您不在memory_order_acquire
方法中使用lock
,则可以在lock
方法之前重新排序对共享数据的任何读取。所以你需要acquire/release
来保护线程之间的共享数据。
spinLock.lock() // use acquire here, so any read can't reordered before `lock`
// Writes to shared data
spinLock.unlock() // use release here, so any write can't reordered after `unlock`
带有acquire/release
的对锁定自旋锁的线程可以看到对共享数据的所有写入。
答案 1 :(得分:3)
关于memory_order_acquire / memory_order_release:考虑使用锁保护的共享数据(a.k.a.内存位置或资源)。我称之为“受保护的数据”。
代码需要确保当lock()返回时,调用者对受保护数据的任何访问都将读取有效值(即不是过时值)。 memory_order_aquire确保插入适当的aquire内存屏障,以便后续读取受保护数据(通过本地CPU缓存)将是有效的。类似地,当调用unlock()时,需要memory_order_release以确保插入适当的内存屏障,以便正确同步其他缓存。
某些处理器不需要获取/释放障碍,并且理论上可能只需要lock()中的完整屏障。但是C ++并发模型需要足够灵活,以支持许多不同的处理器体系结构。
memory_order_relaxed在析构函数中使用,因为这只是一个完整性检查,以确保当前不保持锁定。破坏锁定不会给调用者带来任何同步语义。