c++中如何使用std::condition_variable实现生产者消费者_c++同步【实例】

发布时间 - 2026-01-23 00:00:00    点击率:
std::condition_variable必须与std::mutex配合使用,所有操作须在持有同一把锁的前提下进行;wait()需用while循环防虚假唤醒;notify_one()比notify_all()更高效,适用于典型生产者-消费者场景。

std::condition_variable 必须和 std::mutex 配合使用

单独声明 std::condition_variable 没有意义,它不保存状态,也不提供原子操作。所有 wait()notify_one()notify_all() 调用都必须在持有同一把 std::mutex 的前提下进行,否则行为未定义(常见崩溃或死锁)。

典型错误是:在 wait() 前没加锁,或在 notify_xxx() 时没锁、或用了不同 mutex —— 这些

都会导致程序随机失败,尤其在多核机器上更难复现。

  • wait() 会自动释放传入的 std::unique_lock<:mutex>,并在被唤醒后重新获取锁,所以必须传入已锁定的锁对象
  • notify_one()notify_all() 不要求当前线程持有锁,但为避免竞态(例如通知时消费者刚检查完条件但还没 wait),**强烈建议在持有锁的上下文中调用**
  • 不要用 std::lock_guard 替代 std::unique_lock:前者不可转移、不可手动解锁,无法满足 wait() 的内部解锁需求

必须用 while 循环检查条件,不能用 if

std::condition_variable::wait() 可能被虚假唤醒(spurious wakeup),即没有被 notify 就返回。C++ 标准允许这种行为,且各平台实现均存在。用 if 判断一次就进入临界区,会导致逻辑错乱(比如从空队列取数据)。

正确做法是把条件判断写在 while 循环中,确保每次从 wait() 返回后都重新验证业务条件是否真正满足。

  • 错误写法:if (queue.empty()) cv.wait(lock);
  • 正确写法:while (queue.empty()) cv.wait(lock);
  • 生产者同理:用 while (queue.size() >= capacity) 判断是否满,而非 if

一个可运行的双线程生产者-消费者实例

以下代码用一个固定容量的 std::queue 模拟缓冲区,两个线程分别执行生产和消费,共享一个 std::mutex 和一个 std::condition_variable。注意:所有对 queue 的读写都受同一把锁保护,cv 仅用于阻塞/唤醒协调。

#include 
#include 
#include 
#include 
#include 
#include 

std::queue buffer;
std::mutex mtx;
std::condition_variable cv_producer, cv_consumer;
const size_t CAPACITY = 3;

void producer() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock lock(mtx);
        while (buffer.size() == CAPACITY) {
            cv_producer.wait(lock); // 等待有空位
        }
        buffer.push(i);
        std::cout << "Produced: " << i << "\n";
        lock.unlock(); // 手动解锁,避免 notify 时还持锁(非必须,但更清晰)
        cv_consumer.notify_one(); // 唤醒一个消费者
    }
}

void consumer() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock lock(mtx);
        while (buffer.empty()) {
            cv_consumer.wait(lock); // 等待有数据
        }
        int val = buffer.front();
        buffer.pop();
        std::cout << "Consumed: " << val << "\n";
        lock.unlock();
        cv_producer.notify_one(); // 唤醒一个生产者
    }
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

notify_one() vs notify_all() 的实际影响

在生产者-消费者模型中,通常用 notify_one() 就够了:一个新元素入队,只需唤醒一个等待的消费者;一个元素出队,只需唤醒一个等待的生产者。滥用 notify_all() 会造成“惊群效应”——多个线程同时被唤醒、竞争锁、大部分又立刻回到等待,浪费 CPU 和调度开销。

只有在以下情况才考虑 notify_all()

  • 条件变量关联多个互斥条件(比如同时等待“非空”或“非满”)
  • 无法确定哪个等待线程的条件已满足(如优先级队列场景)
  • 使用 wait_for()wait_until() 且需统一处理超时后的批量清理

多数简单队列场景里,notify_one() 更轻量、更可控。但要注意:如果唤醒的线程因条件不满足又立即 wait,而其他线程本可推进,说明逻辑或唤醒时机有问题 —— 这类问题往往藏在循环条件或 notify 位置里。


# ai  # c++  # ios  # stream  # 有锁  # if  # while  # 循环  # 线程  # 对象  # 多个  # 解锁  # 只需  # 死锁  # 多核  # 前提下  # 也不  # 还没  # 适用于  # 并在 


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


相关推荐: 如何在IIS中配置站点IP、端口及主机头?  黑客如何通过漏洞一步步攻陷网站服务器?  Laravel怎么连接多个数据库_Laravel多数据库连接配置  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询  桂林网站制作公司有哪些,桂林马拉松怎么报名?  Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置  Win11怎么修改DNS服务器 Win11设置DNS加速网络【指南】  如何在阿里云购买域名并搭建网站?  为什么要用作用域操作符_php中访问类常量与静态属性的优势【解答】  LinuxShell函数封装方法_脚本复用设计思路【教程】  Laravel怎么实现一对多关联查询_Laravel Eloquent模型关系定义与预加载【实战】  Python正则表达式进阶教程_复杂匹配与分组替换解析  Laravel如何创建自定义Facades?(详细步骤)  装修招标网站设计制作流程,装修招标流程?  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  如何续费美橙建站之星域名及服务?  Laravel如何为API生成Swagger或OpenAPI文档  如何在IIS7中新建站点?详细步骤解析  香港服务器建站指南:免备案优势与SEO优化技巧全解析  php打包exe后无法访问网络共享_共享权限设置方法【教程】  标题:Vue + Vuex 项目中正确使用 JWT 进行身份认证的实践指南  香港服务器建站指南:外贸独立站搭建与跨境电商配置流程  详解Android中Activity的四大启动模式实验简述  北京的网站制作公司有哪些,哪个视频网站最好?  如何快速查询域名建站关键信息?  php结合redis实现高并发下的抢购、秒杀功能的实例  如何在Windows环境下新建FTP站点并设置权限?  Firefox Developer Edition开发者版本入口  Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】  JavaScript实现Fly Bird小游戏  php json中文编码为null的解决办法  Laravel如何实现API版本控制_Laravel版本化API设计方案  Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言  深圳网站制作平台,深圳市做网站好的公司有哪些?  Laravel软删除怎么实现_Laravel Eloquent SoftDeletes功能使用教程  微信公众帐号开发教程之图文消息全攻略  Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面  如何在Windows 2008云服务器安全搭建网站?  如何在云主机快速搭建网站站点?  HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】  JavaScript如何实现倒计时_时间函数如何精确控制  Bootstrap整体框架之CSS12栅格系统  Python图片处理进阶教程_Pillow滤镜与图像增强  Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制  如何有效防御Web建站篡改攻击?  如何在云服务器上快速搭建个人网站?  如何快速搭建高效服务器建站系统?  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  Laravel如何发送系统通知?(Notification渠道示例)