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

发布于 2024-11-19 11:41:59 字数 1470 浏览 6 评论 0原文

我正在尝试实现一个队列,如果该队列为空,则该队列会阻塞 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 关键部分的包装器,方法 Enter 和 Leave 是私有的,而 CGuard 是它唯一的朋友
  • CGuard 是 CCriticalSection 的 RAII 包装器,在构造函数中进入关键部分,将其留在析构函数中
  • CEvent 是包装器对于 Windows 事件, wait 使用 WaitForSingleObject 函数
  • 我不介意元素按值传递,它们是小对象,
  • 我不能使用 Boost,只是 Windows 的东西(就像我有的那样)已经使用 CEvent 和 CGuard 进行了)

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

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

I'm trying to implement a queue which blocks on the Pop operation if it's empty, and unblocks as soon as a new element is pushed. I'm afraid I might have some race condition; I tried to look at some other implementation, but most I found were done in .NET, and the few C++ I found depended too much on other library classes.

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;
    }
};

Some explanations are due:

  • CCriticalSection is a wrapper for a Windows Critical Section, methods Enter and Leave are private, and CGuard is its only friend
  • CGuard is a RAII wrapper for CCriticalSection, enters critical section on constructor, leaves it on destructor
  • CEvent is a wrapper for a Windows Event, wait uses the WaitForSingleObject function
  • I don't mind that Elements are passed around by value, they are small objects
  • I can't use Boost, just Windows stuff (as I have already been doing with CEvent and CGuard)

I'm afraid there might be some weird race condition scenario when using Pop(). What do you guys think?

UPDATE: Since I'm working on Visual Studio 2010 (.NET 4.0), I ended up using the unbounded_buffer class provided by the C++ runtime. Of course, I wrapped it in a class using the Pointer to Implementation Idiom (Chesire Cat) just in case we decide to change the implementation or need to port this class to another environment

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

愛上了 2024-11-26 11:41:59

它不是线程安全的:

    {//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 调用都将失败。

It’s not thread safe:

    {//RAII block
        CGuard g( m_csQueue );
        bool wait = m_Queue.empty();
    }
    /// BOOM! Other thread ninja-Pop()s an item.
    if( wait )
        m_eElementPushed.wait();

Notice the location of the BOOM comment. In fact, other locations are also thinkable (after the if). In either case, the subsequent front and pop calls will fail.

南渊 2024-11-26 11:41:59

如果您的目标是条件变量应该会有所帮助最新的 Windows 版本。这通常使得阻塞队列的实现更加简单。

请参阅此处了解使用 Boost 设计类似的队列 - 即使您不能使用 Boost 或条件变量,那里的一般指导和后续讨论应该是有用的。

Condition variables should be helpful if you are targetting newest Windows versions. This typically makes implementing blocking queues simpler.

See here for design of a similar queue using Boost - even if you cannot use Boost or condition variables, the general guidance and follow-up discussion there should be useful.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文