阻塞队列实现:争用条件在哪里?
Blocking Queue implementation: Where's the race condition?
又是我和我的BlockingQueue…我根据这篇文章和这个问题重写了它。它发送一些项,然后由于访问冲突而崩溃。下面是代码:
template <typename T>
bool DRA::CommonCpp::CTBlockingQueue<T>::Push( T pNewValue ){
volatile long oldSize;
::InterlockedExchange( &oldSize, m_Size );
CTNode* pNewNode = new CTNode();
pNewNode->m_pValue = pNewValue;
{//RAII block
CGuard g( m_TailCriticalSection );
m_pTailNode->m_pNext = pNewNode;
m_pTailNode = pNewNode;
::InterlockedIncrement( &m_Size );
}
if( oldSize == 0 )
m_eAtLeastOneElement.set();
return true;
}
template <typename T>
bool DRA::CommonCpp::CTBlockingQueue<T>::Pop( T& pValue ){
CTNode* pCurrentNode;
{//RAII block
CGuard g( m_HeadCriticalSection );
pCurrentNode = m_pHeadNode;
CTNode* pNewHeadNode = m_pHeadNode->m_pNext;
if( pNewHeadNode == NULL ){
CEvent* pSignaledEvent;
CEvent::waitForPair( m_eAtLeastOneElement, m_eFinished, pSignaledEvent );
if( pSignaledEvent == &m_eFinished )
return false;
pNewHeadNode = m_pHeadNode->m_pNext;
}
pValue = pNewHeadNode->m_pValue;
m_pHeadNode = pNewHeadNode;
::InterlockedDecrement( &m_Size );
}
delete pCurrentNode;
return true;
}
它总是在调用Pop()时崩溃,在if之后的那行,上面写着:
pValue = pNewHeadNode->m_pValue
会导致cos的pNewHeadNode为NULL。但这是怎么发生的呢?
Edit:忘记初始化代码:
template <typename T>
DRA::CommonCpp::CTBlockingQueue<T>::CTBlockingQueue():
m_HeadCriticalSection("CTBlockingQueue<T>::m_Head"),
m_TailCriticalSection("CTBlockingQueue<T>::m_Tail"){
CTNode* pDummyNode = new CTNode();
m_pHeadNode = pDummyNode;
m_pTailNode = pDummyNode;
m_Size = 0; //Dummy node doesn't count
}
我的假设是事件设置在临界区之外,这意味着push可能会通知该事件两次。你试过在临界区内设置事件吗?
最后,我回到了我最初的,效率较低的实现,即我在这里发布的那个,添加了一个Finish()方法,以便生产者可以向消费者发出优雅的结束信号,以及一个Restart()方法,以便在不破坏和重新创建队列的情况下重新开始生产:
//Template definitions
template<class Element>
DRA::CommonCpp::CTBlockingQueue<Element>::CTBlockingQueue( unsigned int maxSize ):
m_csFinished( "CTBlockingQueue::m_csFinished" ),
m_csQueue( "CTBlockingQueue::m_csQueue" ),
m_semElementCount( 0, maxSize ),
m_bFinished(false){
}
template<class Element>
DRA::CommonCpp::CTBlockingQueue<Element>::~CTBlockingQueue(){
Finish();
}
template<class Element>
void DRA::CommonCpp::CTBlockingQueue<Element>::Push( Element newElement ){
{//RAII block
CGuard g( m_csQueue );
m_Queue.push( newElement );
}
m_semElementCount.Signal();
}
template<class Element>
bool DRA::CommonCpp::CTBlockingQueue<Element>::Pop( Element& element ){
m_semElementCount.Wait();
{//RAII block
CGuard g( m_csFinished );
if( m_bFinished ){
CGuard g( m_csQueue );
if ( m_Queue.size() == 0 )
return false;
}
}
{//RAII block
CGuard g( m_csQueue );
element = m_Queue.front();
m_Queue.pop();
}
return true;
}
template<class Element>
void DRA::CommonCpp::CTBlockingQueue<Element>::Finish(){
{//RAII block
CGuard g( m_csFinished );
m_bFinished = true;
}
{//RAII block
CGuard g( m_csQueue );
m_semElementCount.Signal();
}
}
template<class Element>
void DRA::CommonCpp::CTBlockingQueue<Element>::Restart(){
{//RAII block
CGuard g( m_csFinished );
m_bFinished = false;
}
}
这不是最快的方法,但对我来说已经足够快了。
相关文章:
- 并行块(线程清理器)之外的 OpenMP 中的争用条件;误报?
- 如何在C++中创建争用条件
- C++上的手动重置事件(来自 C#)实现:如何避免争用条件
- 作为随机数生成器的争用条件
- 智能指针析构函数争用条件
- 尽管互斥锁,线程中的争用条件
- 在C++中递增和递减全局变量时的争用条件
- 为什么此代码不创建争用条件?
- __has_include() 和后续 #include 之间是否存在争用条件
- 什么保证两个不相关的线程中的不同不相关对象没有(不可避免的)争用条件?
- 此工厂方法是否会导致争用条件?
- 争用条件 2 个线程交替
- 标准::condition_variable 中可能存在的争用条件
- 启动子进程时的争用条件导致从管道读取挂起
- 当只有一个线程写入 c++ 中的布尔变量时,是否存在争用条件
- 避免在增加计数器时出现争用条件
- 是否有 std 或提升容器可以避免其插入和查找方法之间的争用条件
- 增强进程间争用条件预防
- pthread_once() 中的争用条件
- 阻塞队列实现:争用条件在哪里?