我根据下面的msdn文章中概述的原则编写了一个简单的无锁队列,并且还在下面的DXUT锁定免费管道代码中写道:
所以,我有一个生产者/消费者模型设置,我的主线程提供渲染指令,渲染线程消耗可用的消息并发出相应的opengl调用。如果我在每个循环/迭代中睡眠我的主线程足够长的时间,但是如果我没有睡得足够长(或者根本没有),我会得到一个访问冲突异常:
First-chance exception at 0x00b28d9c in Engine.exe: 0xC0000005: Access violation reading location 0x00004104.
Unhandled exception at 0x777715ee in Engine.exe: 0xC0000005: Access violation reading location 0x00004104.
我的调用堆栈是:
ntdll.dll!777715ee()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!777715ee()
ntdll.dll!7776015e()
Engine.exe!RingBuffer<2048>::BeginRead(void * & ppMem=, unsigned long & BytesAvailable=) Line 52 + 0x10 bytes C++
Engine.exe!Thread::ThreadMain(void * lpParam=0x00107d94) Line 41 + 0xf bytes C++
我无法弄清楚问题可能是什么。我无锁队列的代码如下:
template <uint32 BufferSize>
class RingBuffer
{
public:
RingBuffer()
: m_ReadOffset(0)
, m_WriteOffset(0)
{}
~RingBuffer()
{}
bool Empty() const
{
return (m_WriteOffset == m_ReadOffset);
}
void BeginRead(void*& ppMem, uint32& BytesAvailable)
{
const uint32 ReadOffset = m_ReadOffset;
const uint32 WriteOffset = m_WriteOffset;
AppReadWriteBarrier();
const uint32 Slack = (WriteOffset > ReadOffset) ?
(WriteOffset - ReadOffset) :
(ReadOffset > WriteOffset) ?
(c_BufferSize - ReadOffset) :
(0);
ppMem = (m_Buffer + ReadOffset);
BytesAvailable = Slack;
}
void EndRead(const uint32 BytesRead)
{
uint32 ReadOffset = m_ReadOffset;
AppReadWriteBarrier();
ReadOffset += BytesRead;
ReadOffset %= c_BufferSize;
m_ReadOffset = ReadOffset;
}
void BeginWrite(void*& ppMem, uint32& BytesAvailable)
{
const uint32 ReadOffset = m_ReadOffset;
const uint32 WriteOffset = m_WriteOffset;
AppReadWriteBarrier();
const uint32 Slack = (WriteOffset > ReadOffset || WriteOffset == ReadOffset) ?
(c_BufferSize - WriteOffset) :
(ReadOffset - WriteOffset);
ppMem = (m_Buffer + WriteOffset);
BytesAvailable = Slack;
}
void EndWrite(const uint32 BytesWritten)
{
uint32 WriteOffset = m_WriteOffset;
AppReadWriteBarrier();
WriteOffset += BytesWritten;
WriteOffset %= c_BufferSize;
m_WriteOffset = WriteOffset;
}
private:
const static uint32 c_BufferSize = NEXT_POWER_OF_2(BufferSize);
const static uint32 c_SizeMask = c_BufferSize - 1;
private:
byte8 m_Buffer[ c_BufferSize ];
volatile ALIGNMENT(4) uint32 m_ReadOffset;
volatile ALIGNMENT(4) uint32 m_WriteOffset;
};
由于读/写偏移和缓冲区指针从监视窗口看起来很好,我调试起来很困难。不幸的是,当应用程序中断时,我无法从BeginRead函数中查看autos / local变量。如果有人有使用无锁编程的经验,那么对这个问题或一般建议的任何帮助都会有很大的帮助。
答案 0 :(得分:2)
您可能会发现这些感兴趣的文章......
Lock-Free Code: A False Sense of Security
Writing Lock-Free Code: A Corrected Queue
在第一篇文章中,Herb Sutter讨论了另一位作者对无锁队列的实现,并指出了一些可能出错的问题。在第二篇文章中,Herb对原始实现进行了一些修改。
作为一项学习练习,尝试构建自己的无锁队列是个不错的主意。但是对于生产工作,您可能更安全地从可靠来源找到预先存在的实施并使用它。例如,Concurrency Runtime提供concurrent_queue类
答案 1 :(得分:1)
你没有任何记忆围栏。对volatile变量的访问只是相互排序,而不是其他操作。
在C ++ 0x中,您可以使用std::atomic<T>
来获取适当的围栏。在此之前,您将需要特定于操作系统的线程API,例如Win32 InterlockedExchange
,或者包含诸如boost :: thread的包装器库。
好的,我看到AppReadWriteBarrier
应该提供一个记忆围栏。它是如何实现的?