在同一个互斥锁上使用的两个std :: unique_lock会导致死锁吗?

时间:2017-10-06 04:43:38

标签: c++ mutex deadlock producer-consumer unique-lock

我在代码审查堆栈交换中找到了这个code,它实现了生产者 - 消费者问题。我在这里发布一段代码。

在给定的代码中,让我们考虑一个场景,当生产者通过调用void add(int num)产生一个值,它获取锁定互斥mubuffer.size()==size_这使得生产者继续等待队列到期到条件变量cond

同时,发生上下文切换,消费者调用函数int remove()消耗值,它尝试获取互斥锁mu上的锁,但是之前已经获取了锁。生产者因此失败并且从不消耗该值,从而导致死锁。

我在哪里错了?因为代码在我运行时似乎运行正常,所以调试它对我没有帮助。

由于

void add(int num) {
        while (true) {
            std::unique_lock<std::mutex> locker(mu);
            cond.wait(locker, [this](){return buffer_.size() < size_;});
            buffer_.push_back(num);
            locker.unlock();
            cond.notify_all();
            return;
        }
    }
    int remove() {
        while (true)
        {
            std::unique_lock<std::mutex> locker(mu);
            cond.wait(locker, [this](){return buffer_.size() > 0;});
            int back = buffer_.back();
            buffer_.pop_back(); 
            locker.unlock();
            cond.notify_all();
            return back;
        }
    }

2 个答案:

答案 0 :(得分:3)

std::condition_variable::wait(lock, predicate)的想法是,你要等到谓词满足并且之后锁定互斥锁。要以原子方式执行此操作(大多数情况下这很重要),您必须首先锁定互斥锁,然后等待将释放它并锁定它以检查谓词。如果满足,则互斥锁保持锁定状态并继续执行。如果没有,互斥锁将再次释放。

答案 1 :(得分:3)

OutOfBound的答案很好,但更确切地了解什么是“原子”是有用的。

对条件变量的wait操作具有前提条件和后置条件,即传入的互斥锁被调用者锁定。 wait操作在内部解锁互斥锁,并且保证不会错过因解锁互斥锁而导致的其他线程的任何notifynotify_all操作。在wait内,互斥锁的解锁和进入等待通知的状态相互之间是原子的。这可以避免睡眠/唤醒比赛。

条件关键部分表单在内部测试谓词。但仍然取决于通知是否正确完成。

从某种意义上说,人们可以认为wait这样做:

while (!predicate()) {
    mutex.unlock();
    /* sleep for a short time or spin */
    mutex.lock();
}

带有通知的条件变量允许中间的注释行有效。这给出了:

while (!predicate()) {
    atomic { /* This is the key part. */
        mutex.unlock();
        sleep_until_notified();
    }
    mutex.lock();
}