我正在尝试实现一个队列,如果它是空的,则阻塞Pop操作,并在推送新元素时立即解除阻塞。我担心我可能会遇到一些竞争条件;我试着看看其他一些实现,但我发现大多数是在.NET中完成的,而我发现的少数C ++在很大程度上依赖于其他库类。
template <class Element>
class BlockingQueue{
DRA::CommonCpp::CCriticalSection m_csQueue;
DRA::CommonCpp::CEvent m_eElementPushed;
std::queue<Element> m_Queue;
public:
void Push( Element newElement ){
CGuard g( m_csQueue );
m_Queue.push( newElement );
m_eElementPushed.set();
}
Element Pop(){
{//RAII block
CGuard g( m_csQueue );
bool wait = m_Queue.empty();
}
if( wait )
m_eElementPushed.wait();
Element first;
{//RAII block
CGuard g( m_csQueue );
first = m_Queue.front();
m_Queue.pop();
}
return first;
}
};
有些解释到期了:
我担心使用Pop()时可能会出现一些奇怪的竞争情况。你们觉得怎么样?
UPDATE :由于我正在使用Visual Studio 2010(.NET 4.0),我最终使用了C ++运行时提供的unbounded_buffer类。当然,我使用Pointer to Implementation Idiom(Chesire Cat)将它包装在一个类中,以防我们决定更改实现或需要将此类移植到另一个环境
答案 0 :(得分:9)
这不是线程安全的:
{//RAII block
CGuard g( m_csQueue );
bool wait = m_Queue.empty();
}
/// BOOM! Other thread ninja-Pop()s an item.
if( wait )
m_eElementPushed.wait();
请注意BOOM
评论的位置。事实上,其他地方也是可以想象的(在if
之后)。在任何一种情况下,后续的front
和pop
调用都将失败。
答案 1 :(得分:4)
Condition variables应该会有所帮助。这通常会使实现阻塞队列变得更简单。
有关使用Boost设计类似队列的信息,请参阅here - 即使您不能使用Boost或条件变量,一般指导和后续讨论也应该有用。