Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;模板生产者消费者封锁队列,无限缓冲区:如何优雅地结束?_C++_Windows_Templates_Concurrency_Producer Consumer - Fatal编程技术网

C++ C++;模板生产者消费者封锁队列,无限缓冲区:如何优雅地结束?

C++ C++;模板生产者消费者封锁队列,无限缓冲区:如何优雅地结束?,c++,windows,templates,concurrency,producer-consumer,C++,Windows,Templates,Concurrency,Producer Consumer,我编写了一个BlockingQueue来与两个线程通信。您可以说它遵循生产者-消费者模式,具有无限缓冲区。因此,我使用一个关键部分和一个信号量来实现它,如下所示: #pragma once #include "Semaphore.h" #include "Guard.h" #include <queue> namespace DRA{ namespace CommonCpp{ template<class Element> class BlockingQueue{

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

#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;
}
bool Queue::Pop( Element& result ) {
    sema.Wait();
    if ( stop_condition ) return false;
    critical_section.Enter();
    result = m_queue.front();
    m_queue.pop;
    critical_section.Leave();
    return true;
}
// Pseudo code
bool Pop(Element& r, timeout)
{
   if(sem.wait(timeout))
   {
      r = m_Queue.front();
      m_Queue.pop();
   }

   return false;
}
#pragma一次
#包括“Semaphore.h”
#包括“Guard.h”
#包括
名称空间DRA{
名称空间CommonCpp{
模板
类阻塞队列{
C临界截面m_csQueue;
CSemaphore m_semElementCount;
std::队列m_队列;
//禁止复制和转让
阻塞队列(constblockingqueue&);
BlockingQueue&运算符=(const BlockingQueue&);
公众:
BlockingQueue(无符号整数maxSize);
~BlockingQueue();
元素Pop();
无效推送(元素新元素);
};
}
}
//模板定义
模板
DRA::CommonCpp::BlockingQueue::BlockingQueue(unsigned int maxSize):
m_csQueue(“阻塞队列::m_csQueue”),
m_SemeElementCount(0,最大大小){
}
模板
DRA::CommonCpp::BlockingQueue::~BlockingQueue(){
//TODO我能在这里做什么?
}
模板
void DRA::CommonCpp::BlockingQueue::Push(元素newElement){
{//RAII区块
CGuard g(m_csQueue);
m_Queue.push(新元素);
}
m_semElementCount.Signal();
}
模板
元素DRA::CommonCpp::BlockingQueue::Pop(){
m_semelement count.Wait();
元素弹出;
{//RAII区块
CGuard g(m_csQueue);
popped=m_Queue.front();
m_Queue.pop();
}
返回弹出;
}
CGuard是一个关键部分的RAII包装器,它在构建时进入,在销毁时离开。CSemaphore是Windows信号量的包装器

到目前为止,很好,线程的通信是完美的。但是,当生产者线程停止生产并结束时,消费者线程已经消费了所有东西,消费者线程将永远挂起Pop()调用


我如何才能告诉消费者以优雅的方式结束?我想发送一个特殊的空元素,但它似乎太草率了。

您需要一种方法来告诉消费者停止。这可能是队列中的一个特殊元素,例如
元素
周围的一个简单包装器结构,或者是一个标志-队列类的一个成员变量(在这种情况下,您希望确保该标志以原子方式处理-查找窗口函数)。然后,您需要在每次消费者醒来时检查其状况。最后,在析构函数中,设置停止条件并向信号量发送信号

还有一个问题-从消费者的
pop()
返回什么。我想要一个布尔返回值和一个
Element&
类型的参数,以便在成功时将结果复制到中

编辑: 大概是这样的:

#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;
}
bool Queue::Pop( Element& result ) {
    sema.Wait();
    if ( stop_condition ) return false;
    critical_section.Enter();
    result = m_queue.front();
    m_queue.pop;
    critical_section.Leave();
    return true;
}
// Pseudo code
bool Pop(Element& r, timeout)
{
   if(sem.wait(timeout))
   {
      r = m_Queue.front();
      m_Queue.pop();
   }

   return false;
}

更改pop以返回可选的增强(或者像标准库使用top/pop来分离任务一样),然后在销毁时发出最后一次信号。

您的信号量实现是否有可用的定时等待功能?在Windows上,这将是指定超时的
WaitForSingleObject()
。如果是这样,Pop()可以这样实现:

#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;
}
bool Queue::Pop( Element& result ) {
    sema.Wait();
    if ( stop_condition ) return false;
    critical_section.Enter();
    result = m_queue.front();
    m_queue.pop;
    critical_section.Leave();
    return true;
}
// Pseudo code
bool Pop(Element& r, timeout)
{
   if(sem.wait(timeout))
   {
      r = m_Queue.front();
      m_Queue.pop();
   }

   return false;
}

通过这种方式,
Pop()
仍然处于阻塞状态,尽管它很容易被中断。即使超时时间很短,这也不会占用大量CPU(是的,这是绝对必要的,而且可能会引入额外的上下文切换,因此请注意这些注意事项)。

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

在pop方法上,首先锁定,然后检查它是否为空,然后
WaitForSingleObject
——只要WFSO返回,您就会知道队列中至少有一个项目


选中此项

Pop()
转换为
bool TryPop(元素&)
,并添加一个将导致
TryPop()
返回false的
void Finish()
。诸如此类。如果我这样做,消费者将一直调用TryPop并浪费CPU,我希望保持blocking Pop()风格,因为信号量的等待在
TryPop()
中产生信号量上的CPUKeep阻塞,但是让
Finish()
Push()
一样释放信号量。如果队列大小为
0
finished==true
,则返回
false
。我以为您无法返回对局部变量的引用。还是我误解了?我没说退货,我说的是复印件。传递一个对
Pop()
的引用,并让它将元素从队列复制到参数。现在我明白了,这和@Cory Nelson建议的一样(不知道为什么他没有回答)。谢谢,我现在就试试如果你暗示依赖Boost库,我不能用它。“只是窗户的东西,”老板说,“我考虑过了。但我想避免任何形式的投票。我知道,我可以设置一个非常大的超时,但老实说,我更喜欢@Nikolai的solutionGreat文章,它似乎提供了一个更高效的实现。我将检查示例,看看它如何处理制作人结尾。如果完成了,请考虑你的答案,我检查完这篇文章。如果我添加您的建议,它的行为与预期的一样,即不进行轮询。这是一个更好的实现。然而,你没有回答这个问题:我如何优雅地结束生产者和消费者?我试着查看文章中的演示应用程序,但它有2000行MFC,这让我很吃惊如果你使用的是事件,你可以使用WaitForMultipleObjects。WFMO将等待两个同步对象:第一个是停止事件,第二个是新消息事件。当设置第一个事件时,您知道是时候退出了。我需要告诉如何触发停止吗?:)