单生产者/多消费者僵局

时间:2019-07-15 18:24:57

标签: c++ multithreading deadlock producer-consumer

以下代码导致死锁。问题是我无法弄清楚如何释放等待条件变量的消费者。满足特定条件时,使用者应从堆栈中循环并进行消耗。我试过在堆栈为空时退出,但是当然不起作用。

Stack.h

slow

Stack.cpp

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();
};

main.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) {}

2 个答案:

答案 0 :(得分:0)

好像您的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();
}

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

答案 1 :(得分:0)

您的代码没有死锁,但是您的线程正在等待更多输入,因为您没有正确配置完成的值。 不可能在这里调用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。

此外,正如madducci回答的那样,您需要更改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();
    }