InterlockedExchange vs InterlockedCompareExchange自旋锁

时间:2013-08-12 15:19:49

标签: c++ multithreading atomic

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

2 个答案:

答案 0 :(得分:5)

首先,InterlockedExchange需要LONG。请在我之后重复:LONGint不一样。这似乎是一件小事,但它可能会让你感到悲伤。

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

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

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

这些只是这个代码的两个中度微妙的问题。还有其他人。简单的事实是锁定不容易,你不应该实现自定义锁定原语。请不要重新发明轮子。使用操作系统提供给您的工具。他们自己不太可能成为瓶颈。

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

答案 1 :(得分:3)

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

我认为主要区别在于,在一个对锁具有很多争用的SMP系统中,当值已经“锁定”时,你不会得到一堆写入 - 这意味着其他处理器不会必须读回缓存中已存在的值。

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