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

C++ condition_variable带谓词wait的异常行为及可靠性确认

C++ condition_variable带谓词的wait行为确认

我修改了C++ condition_variable的示例代码,尝试让变量在等待启动前被通知,最终得到一段完全不发送通知但所有线程都被唤醒的代码。发现几个有趣的现象:

  • 仅调用cv.wait(lk)会导致无限等待
  • 带谓词的cv.wait(lk, []{return i == 1;})在谓词满足时不会进入等待
  • cv.wait(lk, []{return true;})不等价于cv.wait(lk)

我需要确认这种行为是否属于预期情况,以及能否依赖该特性:比如调用cond_var.wait(lock, []() { return !packet_queue.empty(); })时,若队列非空,无需收到通知就不会进入等待。

测试代码

#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
 
std::condition_variable cv;
std::mutex cv_m; // This mutex is used for three purposes:
                 // 1) to synchronize accesses to i
                 // 2) to synchronize accesses to std::cerr
                 // 3) for the condition variable cv
int i = 0;
 
void waits(int id)
{
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::unique_lock<std::mutex> lk(cv_m);
    for (int t=10; t!=0; t--)
    {
        std::cerr << id << " waiting " << t << " seconds... \n";
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    // cv.wait(lk);
    cv.wait(lk, []{return i == 1;});
    for (int t=10; t!=0; t--)
    {
        std::cerr << id << " finish waiting " << t << " seconds... \n";
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}
 
void signals()
{
//    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lk(cv_m);
        std::cerr << "Notifying...\n";
    }
//    cv.notify_all();
 
//    std::this_thread::sleep_for(std::chrono::seconds(1));
 
    {
        std::lock_guard<std::mutex> lk(cv_m);
        i = 1;
        std::cerr << "Notifying again...\n";
    }
//    cv.notify_one();
}
 
int main()
{
    std::thread t1(waits, 0), t2(waits, 1), t3(waits, 2), t4(signals);
    t1.join(); 
    t2.join(); 
    t3.join();
    t4.join();
}

行为解释与确认

这种行为完全符合C++标准的预期,可以放心依赖该特性:

  1. 带谓词的wait执行逻辑
    根据C++标准,cv.wait(lk, pred)等价于以下代码:
while (!pred()) {
    cv.wait(lk);
}

也就是说,调用带谓词的wait时,会首先检查谓词是否满足:

  • 如果满足,直接返回,不会进入等待状态
  • 如果不满足,才会释放锁并进入等待,直到收到通知后重新检查谓词

这就是测试代码中线程直接跳过等待的原因:当i=1已经被设置后,第一次检查谓词就满足条件,无需等待任何通知。

  1. 两种wait调用的核心区别
  • cv.wait(lk)会无条件释放锁并进入等待,必须收到通知(或遭遇虚假唤醒)才能被唤醒
  • cv.wait(lk, []{return true;})会先检查谓词,因谓词永远为true,直接返回,不会进入等待状态。这和cv.wait(lk)的行为完全不同。
  1. 队列非空场景的可靠性
    对于cond_var.wait(lock, []() { return !packet_queue.empty(); })这类场景,完全可以依赖该特性:调用wait时若队列已非空,谓词返回true,wait直接返回,无需等待通知。这是条件变量的标准正确用法,既规避了虚假唤醒问题,也处理了“条件在wait前已满足”的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:03:32