2017-10-06 55 views
2

我发现这个code在代码审查堆栈交换,它实现了生产者 - 消费者问题。我在这里发布一段代码。在同一个互斥量上使用两个std :: unique_lock会导致死锁?

在给定的代码,让我们考虑一个场景时,制片人致电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; 
     } 
    } 
+0

等待应释放锁定,直到条件满足并在退出等待之前重新获取它 –

+1

'while(true){..;返回;}'...为什么使用循环? – Jarod42

回答

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

的想法std::condition_variable::wait(lock, predicate),是您在等待谓词得到满足,并且对互斥锁之后。要以原子方式执行此操作(大部分时间都很重要),必须先锁定互斥锁,然后等待释放并锁定它以检查谓词。如果满足,互斥锁保持锁定状态并继续执行。否则,互斥体将再次释放。

相关问题