两个std :: unique_lock在同一静音上使用会导致死锁

Two std::unique_lock used on same mutex causes deadlock ?

本文关键字:死锁 unique std lock 两个      更新时间:2023-10-16

我在代码评论stack Exchange上找到了实现生产者消费者问题的代码。我在此处发布一部分代码。

在给定代码中,让我们考虑一个场景,当生产者通过调用void add(int num)产生值时,它在Mutex mubuffer.size()==size_上获取锁定,这使得生产者由于条件可变cond

同时,发生了上下文开关,消费者调用功能int remove()要消耗值,它试图在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;
        }
    }

std::condition_variable::wait(lock, predicate)的想法是,您要等到谓词达到谓词,然后在Mutex上锁定。要在原子上进行此操作(大多数情况下这很重要),您必须先锁定互斥X,然后等待将其释放并锁定它以检查谓词。如果满足,则静音锁定锁定,执行继续。如果不是,则互斥X将再次发布。

淘汰的答案很好,但是对什么是"原子"的详细信息很有用。

条件变量上的wait操作具有前提条件和后结构,即呼叫者锁定了静音者中传递的情况。wait操作在内部解锁互斥X,并以保证不会错过任何notifynotify_all操作的方式从其他线程中丢失任何因解锁互斥X而发生的线程。在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();
}