阻止队列的这种实现安全吗?

时间:2011-07-08 12:50:26

标签: c++ windows concurrency queue blocking

我正在尝试实现一个队列,如果它是空的,则阻塞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;
    }
};

有些解释到期了:

  • CCriticalSection是Windows Critical Section的包装器,Enter和Leave方法是私有的,CGuard是它唯一的朋友
  • CGuard是CCriticalSection的RAII包装器,在构造函数上输入关键部分,将其留在析构函数上
  • CEvent是Windows事件的包装器,wait使用WaitForSingleObject函数
  • 我不介意元素按值传递,它们是小对象
  • 我不能使用Boost,只能使用Windows(因为我已经使用过CEvent和CGuard)

我担心使用Pop()时可能会出现一些奇怪的竞争情况。你们觉得怎么样?

UPDATE :由于我正在使用Visual Studio 2010(.NET 4.0),我最终使用了C ++运行时提供的unbounded_buffer类。当然,我使用Pointer to Implementation Idiom(Chesire Cat)将它包装在一个类中,以防我们决定更改实现或需要将此类移植到另一个环境

2 个答案:

答案 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之后)。在任何一种情况下,后续的frontpop调用都将失败。

答案 1 :(得分:4)

如果您要定位最新的Windows版本,

Condition variables应该会有所帮助。这通常会使实现阻塞队列变得更简单。

有关使用Boost设计类似队列的信息,请参阅here - 即使您不能使用Boost或条件变量,一般指导和后续讨论也应该有用。