在同一个互斥量上使用两个std :: unique_lock会导致死锁?
我发现这个code在代码审查堆栈交换,它实现了生产者 - 消费者问题。我在这里发布一段代码。在同一个互斥量上使用两个std :: unique_lock会导致死锁?
在给定的代码,让我们考虑一个场景时,制片人致电void add(int num)
产生价值,它获取的是互斥锁mu
和buffer.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;
}
}
OutOfBound的答案很好,但有关“原子”究竟是什么的更多细节是有用的。
对条件变量的wait
操作有一个先决条件和后置条件,即调用者锁定传入的互斥锁。 wait
操作可以在内部解锁互斥锁,并且可以保证不会错过任何由于解锁互斥锁而发生的其他线程的notify
或notify_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();
}
的想法std::condition_variable::wait(lock, predicate)
,是您在等待谓词得到满足,并且对互斥锁之后。要以原子方式执行此操作(大部分时间都很重要),必须先锁定互斥锁,然后等待释放并锁定它以检查谓词。如果满足,互斥锁保持锁定状态并继续执行。否则,互斥体将再次释放。
等待应释放锁定,直到条件满足并在退出等待之前重新获取它 –
'while(true){..;返回;}'...为什么使用循环? – Jarod42