有人可以帮助发现我的低锁定列表中的错误吗?

时间:2009-12-11 16:24:22

标签: c++ winapi stack interlocked

我在Windows 32位上用C ++编写了一个低锁定列表。我在使用关键部分方面取得了很大的改进,但是我希望有人能够理智地检查我所做的事情是否正确并且我所做的事情没有任何错误:

#ifndef __LOW_LOCK_STACK_H_
#define __LOW_LOCK_STACK_H_

template< class T > class LowLockStack
{
protected:
    struct Entry
    {
        Entry*  pNext;
        T*              pData;
    };
    union Header
    {
        __int64         m_XChg;
        struct
        {
                Entry*  m_pNext;
                __int16 m_Depth;
                __int16 m_Counter;
        };
    };

    Header      m_Header;
public:
    LowLockStack()
    {
        m_Header.m_pNext        = NULL;
        m_Header.m_Depth        = 0;
        m_Header.m_Counter  = 0;
    }

    ~LowLockStack()
    {
    }

    void PushEntry( T* pData )
    {
        Entry* pEntry   = new Entry;
        pEntry->pData   = pData;

        Header header;
        Header xchg;
        do
        {
            xchg.m_XChg   = m_Header.m_XChg;

            header.m_pNext  = pEntry;
            header.m_Depth  = xchg.m_Depth + 1;
            header.m_Counter = xchg.m_Counter + 1;
            pEntry->pNext  = xchg.m_pNext;
        } while( _InterlockedCompareExchange64( &m_Header.m_XChg, header.m_XChg, xchg.m_XChg ) != xchg.m_XChg );
    }

    T* PopEntry()
    {
        Entry* pEntry   = NULL;
        Header header;
        Header xchg;
        do
        {
            xchg.m_XChg    = m_Header.m_XChg;

            pEntry                  = xchg.m_pNext;
            if ( pEntry == NULL )
            {
                 return NULL;
            }

            header.m_pNext  = pEntry->pNext;
            header.m_Depth  = xchg.m_Depth - 1;

        } while( _InterlockedCompareExchange64( &m_Header.m_XChg, header.m_XChg, xchg.m_XChg ) != xchg.m_XChg );

        T* pRet = pEntry->pData;
        delete pEntry;

        return pRet;
    }

    __int32 GetDepth()
    {
        return m_Header.m_Depth;
    }
};

#endif

如果没有错误(我怀疑;)),那么将其视为参考实现:D

编辑:我已经更新了代码,考虑了一些批评。

4 个答案:

答案 0 :(得分:4)

正如您所发现的,锁定免费编程很难做到正确。

Windows已经支持无锁单链表http://msdn.microsoft.com/en-us/library/ms684121(VS.85).aspx,你应该尝试使用它而不是自己动手。

答案 1 :(得分:2)

您不同步对列表标题成员的访问权限。这至少在两个层面上是坏的:

  • 为列表标题指定值可能不像您想象的那样原子。这意味着不同步的读操作可能会导致损坏的值。

  • 另一个更可能的问题是,如果你的盒子有多个核心,那么每个核心都可以在处理器缓存中拥有自己的值副本。要使它们同步值,您需要一个内存屏障

答案 2 :(得分:1)

最明显的错误是你给它的名字。无论您将实现为链接列表,您实现的堆栈。

答案 3 :(得分:1)

考虑在列表中真正有两个项目A(B)(head -> A -> Bcount是2:

)时会发生以下事件序列会发生什么
  1. 线程1启动pop()调用,但在 _InterlockedCompareExchange64()之前被抢占
  2. 线程2从堆栈中删除A和B两个项目,然后将两个新项目放回堆栈,顶部项目恰好分配在与A相同的地址,所以我们有,比如说head -> A -> D。请注意,count又回到了2
  3. 线程1恢复,成功执行CAS( _InterlockedCompareExchange64())。现在head指向B,它被解除分配(坏)而D丢失(坏)。

这是一个经典的ABA problem。您应该使用第二个单词作为世代号而不是项目计数,即不要减少它。

现在有关于实验性discussion库的邮件列表boost::lockfree正在进行中。

另请参阅Herb Sutter's lock-free queue - 这是一种不同的方法,其中虚拟节点使生产者和消费者不会相互踩踏。