我正在尝试创建一个生产者-消费者程序,在此程序中,消费者必须继续运行,直到所有生产者都完成为止,然后消耗队列中剩余的内容(如果还有剩余),然后结束。您可以在下面检查我的代码,我想我知道问题出在哪里(可能是死锁),但是我不知道如何使其正常工作。
#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个问题,但没有一个提供我问题的答案。
答案 0 :(得分:2)
当人们将std::atomic
与std::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();