单个生产者/多个消费者死锁

Single producer / multiple consumer deadlock

本文关键字:消费者 死锁 生产者 单个      更新时间:2023-10-16

以下代码在死锁中重新出现。问题是我无法弄清楚如何解锁等待条件变量的消费者。当满足某个条件时,使用者应该从堆栈循环和使用。我尝试在堆栈为空时退出,但当然它不起作用。

Stack.h

class Stack {
private:
    std::stack<int> stack;
    std::mutex mutex;
    std::condition_variable is_empty;
    bool done;
public:
    Stack();
    void push(int);
    void pop();
    void print();
    bool isDone() const;
    ~Stack();
};

堆栈.cpp

#include <iostream>
#include <sstream>
#include <thread>
#include "Stack.h"
void Stack::push(int x) {
    std::lock_guard lock(mutex);
    std::stringstream msg1;
    msg1 << "producer " << std::this_thread::get_id() << " pushing " << x << std::endl;
    std::cout << msg1.str();
    stack.push(x);
    std::stringstream msg;
    msg << "producer " << std::this_thread::get_id() << ": " << x << " pushed" << std::endl;
    std::cout << msg.str();
    is_empty.notify_all();
}
void Stack::pop() {
    std::unique_lock lock(mutex);
    std::stringstream msg;
    msg << "consumer " << std::this_thread::get_id() << " waiting to consume" << std::endl;
    std::cout << msg.str();
    is_empty.wait(lock, [this] { return !stack.empty(); });
    if (!stack.empty()) {
        stack.pop();
        std::stringstream msg1;
        msg1 << "consumer " << std::this_thread::get_id() << " popped" << std::endl;
        std::cout << msg1.str();
    } else {
        done = true;
        is_empty.notify_all();
    }
}
void Stack::print() {
    std::lock_guard lock(mutex);
    for (int i = 0; i < stack.size(); i++) {
        std::cout << "t" << stack.top() << std::endl;
    }
}
Stack::~Stack() {
}
bool Stack::isDone() const {
    return done;
}
Stack::Stack() : done(false) {}

主.cpp

#include <thread>
#include <vector>
#include <iostream>
#include "Stack.h"
int main() {
    Stack stack;
    std::vector<std::thread> producer;
    std::vector<std::thread> consumer;
    for (int i = 0; i < 10; i++) {
        consumer.emplace_back([&stack]{
            while (!stack.isDone()) {
                stack.pop();
            }
        });
    }
    for (int i = 0; i < 1; i++) {
        producer.emplace_back([&stack]{
            for (int j = 0; j < 5; ++j) {
                stack.push(random());
            }
        });
    }
    for (int k = 0; k < producer.size(); k++) {
        producer[k].join();
        std::cout << producer[k].get_id() << " joined" << std::endl;
        stack.print();
    }
    for (int j = 0; j < consumer.size(); j++) {
        consumer[j].join();
        std::cout << consumer[j].get_id() << " joined" << std::endl;
        stack.print();
    }

    return 0;
}

您的代码没有死锁,但您的线程正在等待更多输入,因为您尚未正确配置 done 的值。这里不可能调用 else 条件

is_empty.wait(lock, [this] { return !stack.empty(); });
    if (!stack.empty()) {
        stack.pop();
        std::stringstream msg1;
        msg1 << "consumer " << std::this_thread::get_id() << " popped" << std::endl;
        std::cout << msg1.str();
    } else {
        done = true;
        is_empty.notify_all();
    }

从代码来看,似乎你想要的是,在生产者停止生产后,消费者应该醒来并清空。但这不是实现它的方式。在生产者推送了 5 个元素后,您应该从那里设置 done =true。

同样正如马杜奇所回答的那样,您需要更改 notify_all(( 的位置;

这对我有用

is_empty.wait(lock, [&] { return stack.size()>0 || done; });
    if (!stack.empty()) {
        int val=stack.top();
        stack.pop();
        std::stringstream msg1;
        msg1 << "consumer " << std::this_thread::get_id() << " popped " <<val<<std::endl;
        std::cout << msg1.str();
    }

看起来你的 pop 函数中有一个逻辑错误:如果你从堆栈中弹出一个元素,你从不调用 notify_all((。

正确的方法应该是这样的:

void Stack::pop() {
   std::unique_lock lock(mutex);
   std::stringstream msg;
   msg << "consumer " << std::this_thread::get_id() << " waiting to consume" << std::endl;
   std::cout << msg.str();
   is_empty.wait(lock, [this] { return !stack.empty(); });
   if (!stack.empty()) {
     stack.pop();
     std::stringstream msg1;
     msg1 << "consumer " << std::this_thread::get_id() << " popped" << std::endl;
     std::cout << msg1.str();
   } else {
       done = true;
   }
   is_empty.notify_all();
}

您还可以在主push()之前调用pop()