C++ C++;生产者消费者陷入僵局

C++ C++;生产者消费者陷入僵局,c++,multithreading,producer-consumer,condition-variable,C++,Multithreading,Producer Consumer,Condition Variable,我正在尝试创建一个生产者-消费者程序,在这个程序中,消费者必须一直运行,直到所有生产者都完成,然后消费队列中剩余的内容(如果还有剩余内容),然后结束。你可以在下面查看我的代码,我想我知道问题在哪里(可能是死锁),但我不知道如何使它正常工作 #include<iostream> #include<cstdlib> #include <queue> #include <thread> #include <mutex>

我正在尝试创建一个生产者-消费者程序,在这个程序中,消费者必须一直运行,直到所有生产者都完成,然后消费队列中剩余的内容(如果还有剩余内容),然后结束。你可以在下面查看我的代码,我想我知道问题在哪里(可能是死锁),但我不知道如何使它正常工作

  #include<iostream>
  #include<cstdlib>
  #include <queue>
  #include <thread>
  #include <mutex>
  #include <condition_variable>

  using namespace std;

  class Company{
    public:
        Company() : producers_done(false) {}
        void start(int n_producers, int n_consumers); // start customer&producer threads
        void stop(); // join all threads
        void consumer();
        void producer();
        /* some other stuff */
    private:
        condition_variable cond;
        mutex mut;
        bool producers_done;
        queue<int> products;
        vector<thread> producers_threads;
        vector<thread> consumers_threads;
        /* some other stuff */
  };

void Company::consumer(){
    while(!products.empty()){
        unique_lock<mutex> lock(mut);
        while(products.empty() && !producers_done){
            cond.wait(lock); // <- I think this is where the deadlock happens
        }
        if (products.empty()){
            break;
        }
        products.pop();
        cout << "Removed product " << products.size() << endl;
    }
}

void Company::producer(){
    while(true){
        if((rand()%10) == 0){
          break;
        }
        unique_lock<mutex> lock(mut);
        products.push(1);
        cout << "Added product " << products.size() << endl;
        cond.notify_one();
    }
}

void Company::stop(){
    for(auto &producer_thread : producers_threads){
        producer_thread.join();
    }
    unique_lock<mutex> lock(mut);
    producers_done = true;
    cout << "producers done" << endl;
    cond.notify_all();
    for(auto &consumer_thread : consumers_threads){
        consumer_thread.join();
    }
    cout << "consumers done" << endl;
}

void Company::start(int n_producers, int n_consumers){
  for(int i = 0; i<n_producers; ++i){
    producers_threads.push_back(thread(&Company::producer, this));
  }

  for(int i = 0; i<n_consumers; ++i){
    consumers_threads.push_back(thread(&Company::consumer, this));
  }
}

int main(){
  Company c;
  c.start(2, 2);
  c.stop();

  return true;
}
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
阶级公司{
公众:
Company():producer_done(false){}
void start(int n_生产者,int n_消费者);//启动客户和生产者线程
void stop();//连接所有线程
无效消费者();
无效生产者();
/*一些其他的东西*/
私人:
条件变量cond;
互斥mut;
做得好;
排队产品;
向量发生器和U线程;
矢量线;
/*一些其他的东西*/
};
作废公司::消费者(){
而(!products.empty()){
唯一锁(mut);
while(products.empty()&&!producers\u done){

条件等待(锁定)当人们使用
std::atomic
以及
std::mutex
std::condition_variable
时,几乎100%的情况下会导致死锁。这是因为对该原子变量的修改不受mutex的保护,因此在t之后更新该变量时,条件变量通知会丢失互斥锁已锁定,但在条件变量在使用者中等待之前

修复方法是在保持互斥锁时不使用
std::atomic
,只修改和读取
producers\u done
。例如:

void Company::consumer(){
    for(;;){
        unique_lock<mutex> lock(mut);
        while(products.empty() && !producers_done)
            cond.wait(lock);
        if(products.empty())
            break;
        orders.pop();
    }   
}

当人们使用
std::atomic
以及
std::mutex
std::condition_variable
时,几乎100%的情况下都会导致死锁。这是因为对该原子变量的修改不受mutex的保护,因此在mu之后更新该变量时,条件变量通知会丢失tex已锁定,但条件变量在使用者中等待之前

修复方法是在保持互斥锁时不使用
std::atomic
,只修改和读取
producers\u done
。例如:

void Company::consumer(){
    for(;;){
        unique_lock<mutex> lock(mut);
        while(products.empty() && !producers_done)
            cond.wait(lock);
        if(products.empty())
            break;
        orders.pop();
    }   
}

通常,用一个好的抽象来隔离同步位是一个好主意。您在这里寻找的是一个有界缓冲区。现在您偷偷地集成了我的更改,它是否仍然死锁?您的代码将无法编译。发布a。我不想离主题太远,但一般来说,我们不想让算法吃掉所有system资源。我们应该考虑并设置一个合适的上限(如果合适的话,上限可以很大)。但我的主要观点是,有界缓冲区抽象隔离了所有锁定和等待。生产者所要做的就是制作项目并调用push()。消费者所要做的就是调用pop()并处理项目。(我还希望OP会查找有界缓冲区,看看它是什么。)我已经包括了最小的、完整的和可验证的示例。有一些简化,例如producer中的条件不同(应该由不同的类触发),但是死锁(或其他一些问题)仍然存在。通常用一个好的抽象来隔离同步位是一个好主意。您在这里寻找的是一个有界缓冲区。现在您偷偷地集成了我的更改,它是否仍然死锁?您的代码将无法编译。发布a。我不想离主题太远,但通常我们不想让算法吃东西启动所有系统资源。我们应该考虑并设置一个合适的上限(如果合适的话,上限可以很大)。但我的主要观点是,有界缓冲区抽象隔离了所有的锁定和等待。生产者所要做的就是创建项并调用push()。消费者所要做的就是调用pop()并处理项。(我还希望OP会查找有界缓冲区,看看它是什么。)我已经包括了最小的、完整的和可验证的示例。有一些简化,例如producer中的条件不同(应该由不同的类触发),但是死锁(或其他一些问题)仍然存在。我可能会把你和变量名混淆了-订单和产品应该是一回事-产品。我已经包括了你的建议,程序现在运行得更顺畅了-有时会以死锁结束,有时会结束,但消费者不消费产品。@adamlowlife Post a.Stack overflow不是一个很好的网站交互式调试。如果Maxim已经解决了您原来的问题,那么请接受答案,如果您还有其他问题,请稍后再回来。(另外,请将示例还原为其原始形式。)“当人们使用std::atomic以及std::mutex和std::condition_变量时,几乎100%的情况下会导致死锁。”似乎过于笼统。不一定总是这样。如果您检查修订历史并阅读原始代码,错误相当简单:条件变量condition中未检查终止标志。原子+互斥+条件变量==>死锁在我看来似乎是一个过度陈述。我可能把您与该变量混淆了名称-订单和产品应该是一件事-产品。我已经包括了您的建议,程序现在运行得更为正常-有时会以死锁结束,有时会结束,但消费者不会消费产品。@adamlowlife Post a.Stack overflow不是交互式调试的好网站。如果Maxim解决了您的问题ginal problem,然后请接受答案,如果您还有其他问题,请稍后再回来。(另外,请将示例还原为其原始形式。)“当人们使用std::atomic以及std::mutex和std::condition_变量时,几乎100%的情况下会导致死锁。”