我使用InterlockedExchange编写了一个基本的自旋锁(见下文)。但是我已经看到很多实现使用InterlockedCompareExchange。我的某些不可预见的方式是不正确的,如果不是,每种方式的专业和缺点是什么(如果确实有的话)?
PS我知道睡眠是昂贵的,我想在我打电话之前尝试计数。
class SpinLock
{
public:
SpinLock() : m_lock( 0 ) {}
~SpinLock(){}
void Lock()
{
while( InterlockedExchange( &m_lock, 1 ) == 1 )
{
Sleep( 0 );
}
}
void Unlock()
{
InterlockedExchange( &m_lock, 0 );
}
private:
volatile unsigned int m_lock;
};
答案 0 :(得分:5)
首先,InterlockedExchange
需要LONG
。请在我之后重复:LONG
与int
不一样。这似乎是一件小事,但它可能会让你感到悲伤。
现在,详细说明Mats Petersson所说的话:
你的自旋锁具有可怕的性能,因为Lock
中的InterlockedExchange循环将无条件地修改m_lock
变量,导致后面的处理器完成大量工作。保持缓存一致性的场景。
更糟糕的是,通过不确保您的m_lock
变量本身位于缓存行上,上述影响被放大并可能影响其他数据,不幸的是,与您的实例共享缓存行自旋锁。
这些只是这个代码的两个中度微妙的问题。还有其他人。简单的事实是锁定不容易,你不应该实现自定义锁定原语。请不要重新发明轮子。使用操作系统提供给您的工具。他们自己不太可能成为瓶颈。
如果您确实发现性能问题(即,您具有表明性能瓶颈的分析数据),请首先关注算法更改以及改进并行化和减少锁争用。如果问题仍然存在,那么只有才能查找其他地方。
答案 1 :(得分:3)
CMPXCHG和XCHG之间几乎没有区别(这是你从你提到的两个内在函数中获得的x86指令)。
我认为主要区别在于,在一个对锁具有很多争用的SMP系统中,当值已经“锁定”时,你不会得到一堆写入 - 这意味着其他处理器不会必须读回缓存中已存在的值。
在调试版本中,您还需要确保仅从锁的当前所有者调用Unlock()
!