C ++生产者使用者陷入僵局

时间:2019-03-27 17:34:31

标签: c++ multithreading producer-consumer condition-variable

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

  #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个问题,但没有一个提供我问题的答案。

1 个答案:

答案 0 :(得分:2)

当人们将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();