C ++生产者使用者陷入僵局 [英] C++ Producer Consumer stuck in deadlock

查看:73
本文介绍了C ++生产者使用者陷入僵局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我知道,这里有很多与生产者-消费者相关的问题,我已经浏览了至少10个问题,但没有一个提供我问题的答案.

解决方案

当人们将std::atomicstd::mutexstd::condition_variable一起使用时,几乎在100%的情况下都会导致死锁.这是因为对该原子变量的修改不受互斥量的保护,因此在互斥量被锁定之后但在条件变量在使用者中等待之前更新该变量时,条件变量通知会丢失.

一种解决方法是不使用std::atomic,而仅在持有互斥量时修改并读取producers_done.例如:

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


代码中的另一个错误是,在while(!products.empty())中,它在没有保持互斥锁的情况下调用了products.empty(),导致出现竞争状态.


下一个错误是在等待使用者线程终止时将互斥锁保持锁定状态.修复:

{
    unique_lock<mutex> lock(mut);
    producers_done = true;
    // mutex gets unlocked here.
}
cond.notify_all();

for(auto &consumer_thread : consumers_threads)
    consumer_thread.join();

I'm trying to create a producer-consumer program, where the consumers must keep running until all the producers are finished, then consume what's left in the queue (if there's anything left) and then end. You can check my code bellow, I think I know where the problem (probably deadlock) is, but I don't know how to make it work properly.

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

I know, there are a lot of producer-consumer related questions here, and I've scrolled through at least 10 of them, but none provided answer to my issue.

解决方案

When people use std::atomic along with std::mutex and std::condition_variable that results in deadlock in almost 100% of cases. This is because modifications to that atomic variable are not protected by the mutex and hence condition variable notifications get lost when that variable is updated after the mutex is locked but before condition variable wait in the consumer.

A fix would be to not use std::atomic and only modify and read producers_done while the mutex is held. E.g.:

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


Another error in the code is that in while(!products.empty()) it calls products.empty() without holding the mutex, resulting in a race condition.


The next error is keeping the mutex locked while waiting for the consumer threads to terminate. Fix:

{
    unique_lock<mutex> lock(mut);
    producers_done = true;
    // mutex gets unlocked here.
}
cond.notify_all();

for(auto &consumer_thread : consumers_threads)
    consumer_thread.join();

这篇关于C ++生产者使用者陷入僵局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆