我在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
编辑:我已经更新了代码,考虑了一些批评。
答案 0 :(得分:4)
正如您所发现的,锁定免费编程很难做到正确。
Windows已经支持无锁单链表http://msdn.microsoft.com/en-us/library/ms684121(VS.85).aspx,你应该尝试使用它而不是自己动手。
答案 1 :(得分:2)
您不同步对列表标题成员的访问权限。这至少在两个层面上是坏的:
为列表标题指定值可能不像您想象的那样原子。这意味着不同步的读操作可能会导致损坏的值。
另一个更可能的问题是,如果你的盒子有多个核心,那么每个核心都可以在处理器缓存中拥有自己的值副本。要使它们同步值,您需要一个内存屏障
答案 2 :(得分:1)
最明显的错误是你给它的名字。无论您将实现为链接列表,您实现的是堆栈。
答案 3 :(得分:1)
考虑在列表中真正有两个项目A(B)(head -> A -> B
,count
是2:
pop()
调用,但在 _InterlockedCompareExchange64()
之前被抢占
head -> A -> D
。请注意,count
又回到了2
_InterlockedCompareExchange64()
)。现在head
指向B,它被解除分配(坏)而D丢失(坏)。
这是一个经典的ABA problem。您应该使用第二个单词作为世代号而不是项目计数,即不要减少它。
现在有关于实验性discussion库的邮件列表boost::lockfree正在进行中。
另请参阅Herb Sutter's lock-free queue - 这是一种不同的方法,其中虚拟节点使生产者和消费者不会相互踩踏。