c++如何使用条件变量condition_variable_c++线程通信教程【实战】

发布时间 - 2026-01-31 00:00:00    点击率:
std::condition_variable必须与std::unique_lock配对使用,因wait需临时释放并重获锁;须用while循环检测条件防虚假唤醒;notify应在状态更新后调用且避免锁内执行;销毁前须确保无线程在wait。

condition_variable 必须和 unique_lock 一起用

单独声明 std::condition_variable 没有意义,它不管理互斥状态,只负责挂起/唤醒线程。你必须配对使用 std::unique_lock<:mutex> ——不是 std::lock_guard,也不是 std::shared_lock,更不能裸传 std::mutex

常见错误是写成:

std::mutex mtx;
std::condition_variable cv;
// 错误:lock_guard 作用域一结束就 unlock,cv.wait 会直接抛 std::system_error
std::lock_guard lk(mtx);
cv.wait(lk, []{ return ready; });

正确写法是让锁的生命周期覆盖整个 wait 调用:

std::unique_lock lk(mtx);
cv.wait(lk, []{ return ready; }); // lk 在 wait 返回前始终持有(或被临时释放)
  • wait 内部会自动 unlock() 当前线程的锁

    ,进入等待;被唤醒后重新 lock()
  • unique_lock 是因为它支持手动 unlock() 和转移所有权,lock_guard 不支持
  • 别在 wait 的谓词里做耗时操作,否则阻塞锁时间过长,影响其他线程

虚假唤醒(spurious wakeup)必须用 while 循环检查条件

cv.wait 可能在没有 notify 的情况下返回,这是 POSIX 和 C++ 标准允许的行为。直接用 if 判断会导致逻辑错乱,比如生产者还没写数据,消费者就继续往下读了。

正确模式永远是:

std::unique_lock lk(mtx);
while (!ready) {  // 注意:是 while,不是 if
    cv.wait(lk);
}
// 此时 ready 为 true,且 lk 已重新加锁
  • 即使没被 notify,wait 也可能返回,所以必须循环重检条件变量所依赖的状态
  • 谓词形式 cv.wait(lk, []{ return ready; }) 内部也是 while 循环实现,安全但隐藏了细节
  • 如果你在 wait 里修改了共享状态(比如 pop 队列),记得把修改逻辑也放进循环体,避免多线程竞争导致状态不一致

notify_one 和 notify_all 的选择影响性能和正确性

两者都只是“发信号”,不保证线程立即执行,也不保证唤醒顺序。区别在于:

  • notify_one 唤醒一个在 wait 的线程(通常是等待最久的那个),适合“一对一”场景,比如单生产者-单消费者模型
  • notify_all 唤醒所有 wait 线程,适合“一对多”或条件存在竞争的情况,比如多个线程等同一个资源就绪,但只有一个能抢到

典型陷阱:

// 错误:用 notify_one,但有多个消费者在等同一份数据
// 生产者:
data = 42;
ready = true;
cv.notify_one(); // 只唤醒一个,其余永远卡住

// 正确:如果多个线程可能同时依赖 ready == true,用 notify_all
cv.notify_all();
  • 不要在持有锁期间调用 notify(虽然语法允许),它不需锁保护,提前 unlock 可减少锁争用
  • notify 调用位置很重要:必须在修改完共享状态(如 ready = true)之后、且该状态对等待线程可见之后再调用
  • notify 不会“保存”,如果调用时没有线程在 wait,信号就丢了 —— 所以状态变更和 notify 必须配套,不能靠 notify “触发”状态

condition_variable 不能跨线程销毁

如果还有线程在 wait,此时析构 std::condition_variable 会导致未定义行为(通常 crash 或 hang)。C++11 标准明确要求:销毁前确保没有线程处于 wait 状态。

安全做法是配合标志位 + join:

bool done = false;
std::mutex mtx;
std::condition_variable cv;

// 消费者线程
auto consumer = [&]() {
    std::unique_lock lk(mtx);
    while (!done) {
        cv.wait(lk, [&]{ return ready || done; });
        if (done) break;
        // 处理数据...
        ready = false;
    }
};

// 主线程准备退出
{
    std::unique_lock lk(mtx);
    done = true;
    cv.notify_all(); // 唤醒所有等待线程,让它们检查 done
}
consumer_thread.join(); // 等它退出后再析构 cv 和 mtx
  • 永远不要在析构 mutex 或 condition_variable 前忽略仍在运行的 wait 线程
  • 使用 done 这类退出标志 + notify_all 是最稳妥的协作终止方式
  • 别依赖 std::thread::detach(),分离线程后无法 join,也就无法确认 wait 是否已退出
实际用起来最易出问题的,是把 condition_variable 当作“事件通知器”来用,而忽略了它背后必须绑定可验证的共享状态 —— 没有状态,就没有 wait 的依据,notify 就成了空喊。


# ai  # c++  # 区别  # 作用域  # red  # 有锁  # if  # while  # 循环  # 线程  # 多线程  # Thread  # 事件  # 多个  # 这是  # 也不  # 还没  # 也就  # 你在  # 不要在  # 能在  # 这类  # 很重要 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在IIS中新建站点并解决端口绑定冲突?  php json中文编码为null的解决办法  Laravel怎么清理缓存_Laravel optimize clear命令详解  如何在IIS中新建站点并配置端口与物理路径?  Laravel如何创建自定义Artisan命令?(代码示例)  黑客入侵网站服务器的常见手法有哪些?  三星网站视频制作教程下载,三星w23网页如何全屏?  Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制  Laravel如何使用Socialite实现第三方登录?(微信/GitHub示例)  如何在服务器上配置二级域名建站?  高性能网站服务器配置指南:安全稳定与高效建站核心方案  Laravel如何操作JSON类型的数据库字段?(Eloquent示例)  标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?  Laravel怎么使用Markdown渲染文档_Laravel将Markdown内容转HTML页面展示【实战】  如何在企业微信快速生成手机电脑官网?  网站制作价目表怎么做,珍爱网婚介费用多少?  Java遍历集合的三种方式  如何快速搭建支持数据库操作的智能建站平台?  制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?  Laravel如何配置和使用缓存?(Redis代码示例)  Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置  javascript中的try catch异常捕获机制用法分析  如何用y主机助手快速搭建网站?  Laravel如何实现本地化和多语言支持?(i18n教程)  logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?  JS经典正则表达式笔试题汇总  JavaScript如何实现错误处理_try...catch如何捕获异常?  Laravel如何使用Service Container和依赖注入?(代码示例)  广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?  Laravel如何保护应用免受CSRF攻击?(原理和示例)  微信推文制作网站有哪些,怎么做微信推文,急?  Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】  Laravel如何处理表单验证?(Requests代码示例)  laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程  网站制作壁纸教程视频,电脑壁纸网站?  电商网站制作价格怎么算,网上拍卖流程以及规则?  教你用AI将一段旋律扩展成一首完整的曲子  Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面  如何在局域网内绑定自建网站域名?  Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧  如何在VPS电脑上快速搭建网站?  音乐网站服务器如何优化API响应速度?  Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践  Internet Explorer官网直接进入 IE浏览器在线体验版网址  使用C语言编写圣诞表白程序  Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件  微信小程序 闭包写法详细介绍  北京企业网站设计制作公司,北京铁路集团官方网站?  佛山网站制作系统,佛山企业变更地址网上办理步骤?  ,网页ppt怎么弄成自己的ppt?