C++ 生产者消费者陷入死锁
C++ Producer Consumer stuck in deadlock
我正在尝试创建一个生产者-消费者程序,其中消费者必须保持 运行 直到所有生产者都完成,然后消费队列中剩余的内容(如果还有剩余的话),然后结束.您可以在下面查看我的代码,我想我知道问题出在哪里(可能是死锁),但我不知道如何让它正常工作。
#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 个,但 none 为我的问题提供了答案。
当人们将 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();
我正在尝试创建一个生产者-消费者程序,其中消费者必须保持 运行 直到所有生产者都完成,然后消费队列中剩余的内容(如果还有剩余的话),然后结束.您可以在下面查看我的代码,我想我知道问题出在哪里(可能是死锁),但我不知道如何让它正常工作。
#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 个,但 none 为我的问题提供了答案。
当人们将 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();