我写了一个BlockingQueue以便通信两个线程。您可以说它遵循Producer-Consumer模式,具有无限制的缓冲区。因此,我使用关键部分和信号量来实现它,如下所示:

#pragma once

#include "Semaphore.h"
#include "Guard.h"
#include <queue>

namespace DRA{
namespace CommonCpp{
template<class Element>
class BlockingQueue{
    CCriticalSection    m_csQueue;
    CSemaphore          m_semElementCount;
    std::queue<Element> m_Queue;
//Forbid copy and assignment
    BlockingQueue( const BlockingQueue& );
    BlockingQueue& operator=( const BlockingQueue& );
public:
    BlockingQueue( unsigned int maxSize );
    ~BlockingQueue();
    Element Pop();
    void Push( Element newElement );
};
}
}

//Template definitions
template<class Element>
DRA::CommonCpp::BlockingQueue<Element>::BlockingQueue( unsigned int maxSize ):
    m_csQueue( "BlockingQueue::m_csQueue" ),
    m_semElementCount( 0, maxSize ){
}

template<class Element>
DRA::CommonCpp::BlockingQueue<Element>::~BlockingQueue(){
    //TODO What can I do here?
}

template<class Element>
void DRA::CommonCpp::BlockingQueue<Element>::Push( Element newElement ){
    {//RAII block
        CGuard g( m_csQueue );
        m_Queue.push( newElement );
    }
    m_semElementCount.Signal();
}

template<class Element>
Element DRA::CommonCpp::BlockingQueue<Element>::Pop(){
    m_semElementCount.Wait();
    Element popped;
    {//RAII block
        CGuard g( m_csQueue );
        popped = m_Queue.front();
        m_Queue.pop();
    }
    return popped;
}

CGuard是CCriticalSection的RAII包装器,它在构造时进入,而在破坏时离开。 CSemaphore是Windows信号量的包装。

到目前为止,一切都很好,线程之间的通信非常完美。但是,当生产者线程停止生产并结束,并且消费者线程消耗了所有东西时,消费者线程永远卡在Pop()调用上。

我怎样才能告诉消费者优雅的结局?我曾想发送一个特殊的空元素,但它似乎太草率。

最佳答案

您最好使用事件而不是信号量。添加时,请锁定CS,并检查元素计数(存储到bIsEmpty局部变量中)。添加到队列中,然后检查元素数是否为空,SetEvent!

在pop方法上,首先锁定,然后检查它是否为空,然后检查WaitForSingleObject-WFSO返回时,您会立即知道队列中至少有一项。

检查此article

关于c++ - C++模板化的Producer-Consumer BlockingQueue,无界缓冲区: How do I end elegantly?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6683356/

10-09 12:37