在下面的代码中,我们的任务是使用多线程创建一个消费者/生产者程序,并找到一种防止死锁的方法。
#include <iostream>
#include <stack>
#include <time.h>
#include <mutex>
#include <condition_variable>
#include <random>
#include <thread>
#include <windows.h>
#include <algorithm>
#include <stack>
#define MAX 100
#define MIN 0
std::condition_variable cv;
std::mutex mtx;
class ProducerConsumer{
public:
// Creates a stack
std::stack<int> stack_array;
// Facilitates checking of values
int producer_sum = 0;
int consumer_sum = 0;
// Generates random number
int random_number_generator(){
std::random_device rd;
std::default_random_engine re{rd()};
return re();
}
// Produces values and pushes them into the stack
void producer(){
for (int i = 0 ; i < MAX ; i++){
std::unique_lock<std::mutex> lg(mtx);
if (stack_array.size() == MAX){
std::cout << "Producer Thread " << std::this_thread::get_id() << " is waiting" << std::endl;
cv.wait(lg);
}
int rand_num = random_number_generator()%10+1;
int rand_num_sleep = random_number_generator()%100+1;
std::cout << "Current Stack Size - " << stack_array.size() << " | Pushing: " << rand_num << "... " << "and sleeping for: " << rand_num_sleep << " milliseconds" << std::endl;
producer_sum += rand_num;
stack_array.push(rand_num);
std::this_thread::sleep_for(std::chrono::milliseconds(rand_num_sleep));
cv.notify_all();
lg.unlock();
}
}
void consumer(){
for (int i = 0 ; i < MAX ; i++) {
std::unique_lock<std::mutex> lg(mtx);
if (stack_array.size() == MIN){
std::cout << "Consumer Thread " << std::this_thread::get_id() << " is waiting" << std::endl;
cv.wait(lg);
}
int rand_num_sleep = random_number_generator()%100+1;
std::cout << "Current Stack Size - " << stack_array.size() << " | Popping: " << stack_array.top() << "... " << "and sleeping for: " << rand_num_sleep << " milliseconds" << std::endl;
consumer_sum += stack_array.top();
stack_array.pop();
std::this_thread::sleep_for(std::chrono::milliseconds(rand_num_sleep));
cv.notify_all();
lg.unlock();
}
}
};
int main(){
ProducerConsumer pc;
std::thread producer_one (&ProducerConsumer::producer, &pc);
std::thread consumer_one (&ProducerConsumer::consumer, &pc);
std::thread producer_two (&ProducerConsumer::producer, &pc);
std::thread consumer_two (&ProducerConsumer::consumer, &pc);
producer_one.join();
producer_two.join();
consumer_one.join();
consumer_two.join();
std::cout << "Producer sum: " << pc.producer_sum << std::endl;
std::cout << "Consumer sum: " << pc.consumer_sum << std::endl;
std::cout << "Stack size: " << pc.stack_array.size() << std::endl;
}
当尝试运行代码时,有时候代码会遇到死锁,因为我认为程序是从生产者线程开始的(这是可以理解的,因为我没有编写一种方法来绕过它)。但有时候,代码运行得很好。其他时候,程序会遇到分段错误或在执行过程中遇到死锁。
我假设我的互斥实现是条件变量是正确的。如果不是,谁能解释一下我的代码有什么问题吗?任何帮助都是感激的。非常感谢!
2条答案
按热度按时间ddhy6vgd1#
条件变量wait总是必须在循环中,因为它可能在另一个线程已经获取了该项时醒来,或者它可能无缘无故地醒来。
当然,如果没有循环,编译器不会告诉你错误,我的意思是没有循环的变量是无用的,因为它不可靠。
你的生产者使用
notify_all
,你有两个消费者,这意味着在添加一个项目后,你唤醒两个消费者(和两个生产者),他们都试图获得一个项目。当然,其中一个不会有一个项目-除非另一个生产者也生产了一个项目(偶然)在同一时间。取代:
您只需将
if
更改为while
-在使用者中也是如此。注意,即使你很小心地平衡了通知和消耗的数量,循环也是需要的,因为
wait
可能会无缘无故地返回。在一些操作系统上,有时候,wait
不确定通知是否发生了,所以它无论如何都会停止等待。这被称为"伪唤醒"。然后你检查队列是否满了,如果仍然满了,你就再等一会儿。xqkwcwgp2#
你需要生产者等到某样东西被消费掉,消费者等到某样东西被生产出来。
现在,不管发生了什么,只要线程被唤醒,线程就会继续运行。例如,消费者在唤醒时不会检查堆栈上是否有任何内容,但另一个消费者可能已经获取了它休眠时生成的所有内容。
然后它试图弹出堆栈,程序就爆炸了。
您还需要防止“伪唤醒”。
幸运的是,有一个
wait
重载可以帮助处理这两个问题:以及