You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

condition_variable死锁原因分析及带谓词wait与普通wait的差异

condition_variable两种wait用法的差异分析

你编写的多线程代码期望按first-second-third顺序执行,第一种无标志位的实现出现死锁,而添加布尔标志位并使用带谓词的wait后程序正常执行,核心差异在于是否处理「虚假唤醒」以及「等待条件的正确性校验」,具体分析如下:

一、无谓词的wait(unique_lock)为什么会死锁?

先看你的第一段代码:

class Foo
{
public:
  void printfirst() { printf("first"); }
  void printsecond() { printf("second"); }
  void printthird() { printf("third"); }
  Foo()
  {
  }
  mutex mtx;
  condition_variable cv1, cv2;

  void first()
  {
    lock_guard<mutex> l(mtx);
    printfirst();
    cv1.notify_one();
  }

  void second()
  {
    unique_lock<mutex> ul(mtx);
    cv1.wait(ul);
    printsecond();
    cv2.notify_one();
  }
  void third()
  {
    unique_lock<mutex> ul(mtx);
    cv2.wait(ul);
    printthird();
  }
};

死锁的核心原因有两个:

  1. 唤醒时机不匹配:如果first()线程先执行,它拿到锁打印first后调用cv1.notify_one(),但此时second()线程还没调用cv1.wait(ul)——唤醒信号「发早了」,second()后续进入等待时,会永远等不到唤醒,进而third()也会永远等待,最终整个程序死锁。
  2. 无法处理虚假唤醒:即使唤醒时机对上,condition_variable的wait可能因为系统调度等原因被「虚假唤醒」(没有收到notify就被唤醒),此时second()会直接往下执行,但实际first()还没完成,破坏执行顺序,甚至引发其他问题。

二、带谓词的wait(unique_lock, Predicate)为什么能正常工作?

再看修正后的代码:

class Foo
{
public:
  void printfirst() { printf("first"); }
  void printsecond() { printf("second"); }
  void printthird() { printf("third"); }
  Foo()
  {
    firstready=false;
    secondready=false;
  }
  mutex mtx;
  bool firstready,secondready;
  condition_variable cv1, cv2;

  void first()
  {
    lock_guard<mutex> l(mtx);
    printfirst();
    firstready=true;
    cv1.notify_one();
  }

  void second()
  {
    unique_lock<mutex> ul(mtx);
    cv1.wait(ul,[&]{return firstready;});
    printsecond();
    secondready=true;
    cv2.notify_one();
  }
  void third()
  {
    unique_lock<mutex> ul(mtx);
    cv2.wait(ul,[&]{return secondready;});
    printthird();
  }
};

带谓词的wait本质是语法糖,等价于以下循环逻辑:

while (!predicate()) {
    wait(lock);
}

它会先检查谓词是否为true,如果是就直接返回;如果不是,就释放锁进入等待。被唤醒后,会重新获取锁,再次检查谓词——只有当谓词为true时才会退出等待,否则继续等待。

它解决了两个核心问题:

  1. 处理唤醒时机不匹配:如果first()先执行,设置firstready=true并唤醒cv1,后续second()进入wait时,会先检查firstready是否为true,发现已经满足条件,直接跳过等待执行后续逻辑,不会出现「等不到唤醒」的情况。
  2. 避免虚假唤醒:即使被虚假唤醒,谓词检查会发现条件不满足(比如firstready还是false),会继续回到等待状态,保证只有当真正的条件满足时才会执行后续代码。

三、两种wait用法的核心差异总结

  • 无谓词wait(lock):仅响应唤醒信号,不校验等待条件。存在唤醒信号丢失、虚假唤醒的风险,几乎不会在生产代码中单独使用。
  • 带谓词wait(lock, pred):内置循环逻辑,每次被唤醒后都会重新检查谓词是否满足。既解决了唤醒时机不匹配导致的信号丢失问题,又能抵御虚假唤醒,是condition_variable的常规正确用法。

内容的提问来源于stack exchange,提问作者chenheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:09:23