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

Suj*_*ith 5 c++ mutex deadlock producer-consumer unique-lock

我找到了这个代码,它实现了生产者-消费者问题。我在这里发布一段代码。

在给定的代码中,让我们考虑这样一个场景:生产者通过调用生成一个值void add(int num),它获取互斥体上的锁mu,并且 buffer.size()==size_这使得生产者由于条件变量 而进入等待队列cond

与此同时,发生上下文切换,消费者调用函数int remove()来消费 value ,它尝试获取 mutex 上的锁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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

Zal*_*ern 3

OutOfBound 的答案很好,但是关于“原子”到底是什么的更多细节是有用的。

对条件变量的操作wait有一个前置条件和一个后置条件,即传入的互斥体被调用者锁定。该wait操作在内部解锁互斥体,并以保证不会错过因解锁互斥体而发生的任何其他线程操作的notify方式进行解锁。notify_allwait互斥锁的解锁和进入等待通知的状态中,彼此是原子的。这可以避免睡眠/唤醒竞赛。

条件临界区形式在内部测试谓词。然而,这仍然取决于通知是否正确完成。

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

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

带notify的条件变量可以让中间的注释行变得高效。这使:

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