3

我使用 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;
};
4

2 回答 2

5

首先,InterlockedExchange需要一个LONG. 请跟我重复一遍: aLONG和 an 不同int。这似乎是一件小事,但它可能会让你感到悲伤。

现在,详细说明一下 Mats Petersson 所说的话:

您的自旋锁将具有可怕的性能,因为 InterlockedExchange 循环Lock将无条件地修改m_lock变量,从而导致后台处理器完成大量工作以保持缓存一致性。

更糟糕的是,通过不确保您的m_lock变量本身位于缓存行上,上述影响会被放大并可能影响其他数据,不幸的是与您的自旋锁实例共享缓存行。

这些只是此代码的两个适度微妙的问题。还有其他的。一个简单的事实是,锁定并不容易正确,您不应该实现自定义锁定原语。请不要重新发明轮子。使用操作系统为您提供的设施。它们本身不太可能成为瓶颈。

如果您确实发现存在性能问题(即,您的分析数据表明存在性能瓶颈),请首先关注算法更改以及改进并行化和减少锁争用。如果问题仍然存在,然后再寻找其他地方。

于 2013-08-12T15:56:51.150 回答
4

CMPXCHG 和 XCHG 之间几乎没有区别(这是您从您提到的两个内在函数中获得的 x86 指令)。

我认为主要区别在于,在锁存在大量争用的 SMP 系统中,当值已经“锁定”时,您不会收到大量写入 - 这意味着其他处理器不必读取返回缓存中已经存在的值。

在调试版本中,您还需要确保Unlock()仅从锁的当前所有者调用!

于 2013-08-12T15:42:53.890 回答