讲师中心 微信公众号
AI工具推荐 视频效率加速

C++怎么用条件变量 C++多线程生产者消费者模型【深度】

阿磊吖_1716

阿磊吖_1716

发布时间:2026-03-07 14:29:03

|

528人浏览过

|

来源于php中文网

原创

std::condition_variable 等待 notify_one()/notify_all() 通知,唤醒阻塞的等待线程;必须配 std::unique_lock 以原子地释放锁并等待,唤醒后自动重锁,防止竞态与虚假唤醒。

c++怎么用条件变量 c++多线程生产者消费者模型【深度】

std::condition_variable 等什么、唤醒谁、为什么必须配 std::unique_lock

它不等时间,也不等某个变量值变化,而是等一个「通知」——notify_one() 或 notify_all() 发出的信号。但它不能自己判断条件是否满足,所以你得在 wait() 前手动检查,且这个检查和挂起必须原子:否则会丢通知。

这就是为什么它强制要求传 std::unique_lock<:mutex></:mutex>:锁住临界区的同时,让 wait() 能安全地释放锁并进入等待;被唤醒后又自动重新加锁,避免检查条件和消费数据之间出现竞态。

常见错误现象:wait() 返回后直接操作共享数据,却没再检查条件是否真满足(虚假唤醒)。

  • 永远用 while 循环包裹 wait(),别用 if
  • std::shared_mutex 或 std::shared_lock 不行,condition_variable 只认 unique_lock
  • 锁对象生命周期必须长于 condition_variable,否则析构时可能还在被 wait

示例关键片段:

立即学习“C++免费学习笔记(深入)”;

std::queue<int> q;
std::mutex mtx;
std::condition_variable cv;

// 消费者
while (true) {
    std::unique_lock<std::mutex> lk(mtx);
    cv.wait(lk, []{ return !q.empty(); }); // 虚假唤醒也安全
    int val = q.front();
    q.pop();
    lk.unlock();
    process(val);
}

生产者怎么 notify 才不丢唤醒、不惊群

notify_one() 随机唤醒一个等待线程,notify_all() 唤醒全部。看似后者更“保险”,但实际多数场景该用 notify_one():生产者每 push 一个元素,只需唤醒一个消费者;用 notify_all() 会导致多个消费者争抢同一个元素,其余线程白唤醒一次(惊群),还可能引发不必要的锁竞争。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

容易踩的坑是:在锁外调用 notify_xxx()。虽然语法允许,但若此时没有线程在 wait,通知就丢了;而如果刚好有线程刚释放锁、正要进入 wait,也可能错过。

  • 务必在持有同一把 std::mutex 的前提下,修改完共享状态(如 push 到队列)后,立刻调用 notify_one()
  • 不要在循环里反复 notify_one() —— 每次只对应一次有效消费
  • 如果生产者一次塞多个元素,且希望唤醒多个消费者,应调用 notify_one() 多次,而非一次 notify_all()

queue.empty() 和 size() 在多线程里为什么不能信

std::queue::empty() 和 size() 都不是原子操作,它们内部可能涉及多个成员访问。即使你用锁保护了 push/pop,单独读 empty() 的结果在解锁后可能已失效——别的线程可能已经 push 或 pop 了。

这也是为什么 condition_variable 的 predicate 必须是 while + lambda:不是靠 empty() 判断,而是靠 wait 返回后再次检查真实状态。

  • 永远别写 if (q.empty()) { cv.wait(...); } —— 中间有窗口期
  • 别依赖 q.size() == 0 当作空判断,行为未定义(某些实现可能抛异常)
  • 如果你需要精确计数(比如限流),用独立的 std::atomic<size_t> 维护,而不是查 queue

std::condition_variable 的销毁时机很关键

它本身不持有线程,但若还有线程卡在 wait(),你就析构了它或它依赖的 mutex,程序会 undefined behavior(通常是 crash)。这不是理论风险,而是真实发生在线程还没退出、但主线程已 join() 完毕并开始析构全局/局部对象时。

典型场景:类成员变量是 condition_variable,析构函数没等所有工作线程真正退出就返回。

  • 确保所有调用 wait() 的线程已退出(比如通过标志位 + notify_all() + join)
  • 不要在 signal handler、atexit 回调里操作 condition_variable
  • 如果用 std::jthread(C++20),利用其自动 join 特性可降低出错概率,但仍需显式通知退出

最稳妥的做法:先设置停止标志,再 notify_all(),再 join(),最后才让 condition_variable 和 mutex 随作用域结束。

热门AI工具

更多
UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

354

2023.09.25

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

1031

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

345

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

554

2026.01.05

C++ Lambda 表达式与函数式编程
C++ Lambda 表达式与函数式编程

深入讲解 C++ Lambda 表达式与函数式编程范式,涵盖 Lambda 基础语法与返回值推导、值捕获与引用捕获的区别与陷阱、初始化捕获(C++14 广义捕获)、泛型 Lambda(auto 参数)、mutable 关键字修改捕获变量、std::function 类型擦除与函数包装、std::bind 参数绑定(及其被 Lambda 替代的趋势)、Lambda 在 STL 算法(sort/transform/for_each/remo

709

2026.05.06

Java 函数式接口与 Lambda 进阶技巧
Java 函数式接口与 Lambda 进阶技巧

在 Java Stream 基础之上进一步深入函数式编程,涵盖 @FunctionalInterface 注解与自定义函数式接口设计、四种方法引用(静态/实例/对象/构造器)的使用场景、Lambda 中的 effectively final 变量捕获规则、函数组合(andThen/compose)与柯里化技巧、Comparator 链式比较器构建、CompletableFuture 异步函数式编排、Reactor / RxJava 响应

408

2026.05.08

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4338

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

1209

2025.12.24

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

40

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn