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

C++ Producer Consumer stuck in deadlock

本文关键字:陷入僵局 消费者 生产者 C++      更新时间:2023-10-16

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

  #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::mutex一起使用时,std::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();