首页/新闻资讯/正文详情

Rust std::sync::Condvar 条件变量详解

发布时间:2026/9/27 11:30:22 来源:云帆数科 栏目:资讯中心
Rust std::sync::Condvar 条件变量详解
Rust std::sync::Condvar 条件变量详解一、条件变量详解1、引言2、 什么是条件变量3、 Condvar 的核心 API3.1 、关键设计wait 与锁的配合4、实战生产者-消费者模型4.1、 为什么用 while 而不是 if5、带超时的等待6、 常见陷阱与最佳实践6.1、 必须与 Mutex 配合使用6.2、 防止丢失唤醒6.3、 优先使用 notify_one6.4 、避免持锁调用 notify7、 总结二、代码示例一、条件变量详解1、引言在多线程编程中线程之间不仅需要共享数据还需要在特定条件满足时进行同步与通信。Rust 标准库提供了std::sync::Condvar条件变量用于实现线程间的等待与唤醒机制。本文将深入剖析 Condvar 的原理、用法与常见陷阱帮助你写出安全高效的并发代码。2、 什么是条件变量条件变量Condition Variable是一种同步原语它允许一个或多个线程在某个条件不满足时进入阻塞等待状态并在条件可能满足时被其他线程唤醒。条件变量通常与互斥锁Mutex配合使用核心价值在于避免忙等待线程无需反复轮询检查条件节省 CPU 资源。精确唤醒只有条件真正变化时才唤醒等待线程减少无效竞争。解耦等待与通知生产者与消费者无需直接感知对方的存在。3、 Condvar 的核心 APIRust 的Condvar定义在std::sync模块中常用方法如下方法说明new()创建新的条件变量wait(guard)阻塞当前线程等待通知自动释放锁并重新获取wait_timeout(guard, duration)带超时等待返回是否超时notify_one()唤醒一个等待线程notify_all()唤醒所有等待线程3.1 、关键设计wait 与锁的配合wait方法接收一个MutexGuard其内部执行三个原子步骤释放互斥锁阻塞当前线程被唤醒后重新获取互斥锁。这三个步骤在底层是不可分割的从而避免了「条件刚检查完、锁还没释放就被通知」的竞态问题。4、实战生产者-消费者模型下面通过一个经典的生产者-消费者示例演示 Condvar 的基本用法。usestd::sync::{Arc,Condvar,Mutex};usestd::thread;usestd::time::Duration;fnmain(){// 共享队列数据 条件变量letpairArc::new((Mutex::new(Vec::new()),Condvar::new()));letpair_cloneArc::clone(pair);// 生产者线程letproducerthread::spawn(move||{let(lock,cvar)*pair_clone;foriin0..5{letmutqueuelock.lock().unwrap();queue.push(i);println!(生产者: 放入 {},i);// 通知消费者cvar.notify_one();drop(queue);thread::sleep(Duration::from_millis(500));}});// 消费者线程letconsumerthread::spawn(move||{let(lock,cvar)*pair;loop{letmutqueuelock.lock().unwrap();// 条件不满足时等待whilequeue.is_empty(){queuecvar.wait(queue).unwrap();}letvaluequeue.remove(0);println!(消费者: 取出 {},value);ifvalue4{break;}}});producer.join().unwrap();consumer.join().unwrap();println!(任务完成);}4.1、 为什么用 while 而不是 if在等待条件时必须使用while循环反复检查条件原因有二虚假唤醒操作系统可能在没有notify的情况下唤醒线程多消费者竞争多个消费者被唤醒后其中一个可能已经消费掉数据其余线程需要重新检查。5、带超时的等待在某些场景下我们不希望线程无限期阻塞。wait_timeout可以设置最长等待时间usestd::sync::{Arc,Condvar,Mutex};usestd::thread;usestd::time::Duration;fnmain(){letpairArc::new((Mutex::new(false),Condvar::new()));letpair_cloneArc::clone(pair);// 等待线程letwaiterthread::spawn(move||{let(lock,cvar)*pair_clone;letmutreadylock.lock().unwrap();let(guard,timeout)cvar.wait_timeout(ready,Duration::from_secs(2)).unwrap();readyguard;iftimeout.timed_out(){println!(等待超时条件仍未满足);}else{println!(条件已满足: {},*ready);}});thread::sleep(Duration::from_secs(1));// 通知线程let(lock,cvar)*pair;letmutreadylock.lock().unwrap();*readytrue;cvar.notify_one();drop(ready);waiter.join().unwrap();}6、 常见陷阱与最佳实践6.1、 必须与 Mutex 配合使用Condvar 本身不保护共享数据必须搭配Mutex使用。wait需要传入MutexGuard这正是为了在等待期间安全地释放锁。6.2、 防止丢失唤醒在检查条件与调用wait之间不能有其他线程插入修改条件。Rust 的wait将「释放锁 阻塞」合并为原子操作从语言层面规避了这个问题。6.3、 优先使用 notify_one当只有一个线程需要被唤醒时使用notify_one比notify_all更高效可以减少线程竞争与上下文切换。6.4 、避免持锁调用 notify虽然 Rust 允许在持锁状态下调用notify但建议先释放锁再通知这样可以避免唤醒的线程立即因抢锁失败而再次阻塞提升性能。7、 总结Condvar 是 Rust 并发编程中实现线程间条件同步的重要工具。掌握其核心 API 与「while 循环检查条件」的规范写法能够帮助你构建健壮的多线程应用。在实际开发中也可以考虑使用std::sync::mpsc通道或第三方库如crossbeam来简化某些场景的通信逻辑。二、代码示例usestd::sync::{Arc,Condvar,Mutex};usestd::thread;usestd::time::Duration;fndemo_basic_notify_one(){println!( demo_basic_notify_onenotify_one 唤醒单个线程 );letpairArc::new((Mutex::new(false),Condvar::new()));letpair_cloneArc::clone(pair);lethandlethread::spawn(move||{let(lock,cvar)*pair_clone;letmutguardlock.lock().unwrap();while!*guard{println!(子线程条件不满足进入 wait 等待);guardcvar.wait(guard).unwrap();}println!(子线程收到唤醒信号条件为 true继续执行);});thread::sleep(Duration::from_millis(800));let(lock,cvar)*pair;{letmutguardlock.lock().unwrap();*guardtrue;println!(主线程修改条件执行 notify_one());cvar.notify_one();// 离开这个大括号guard 自动 drop释放锁}handle.join().unwrap();println!();}fndemo_notify_all(){println!( demo_notify_allnotify_all 广播唤醒全部等待线程 );letdataArc::new((Mutex::new(false),Condvar::new()));letmuthandlesVec::new();foriin0..3{letdata_cloneArc::clone(data);leththread::spawn(move||{let(lock,cvar)*data_clone;letmutguardlock.lock().unwrap();while!*guard{println!(线程{}等待唤醒,i);guardcvar.wait(guard).unwrap();}println!(线程{}被 notify_all 唤醒,i);});handles.push(h);}thread::sleep(Duration::from_millis(600));let(lock,cvar)*data;{letmutglock.lock().unwrap();*gtrue;println!(主线程设置条件 truenotify_all());cvar.notify_all();}forhinhandles{h.join().unwrap();}println!();}fndemo_wait_timeout(){println!( demo_wait_timeout带超时等待两种结果唤醒 / 超时 );letpairArc::new((Mutex::new(false),Condvar::new()));letpair_cloneArc::clone(pair);lethandlethread::spawn(move||{let(lock,cvar)*pair_clone;letmutguardlock.lock().unwrap();while!*guard{letrescvar.wait_timeout(guard,Duration::from_secs(1)).unwrap();guardres.0;ifres.1.timed_out(){println!(子线程wait_timeout 超时返回);break;}else{println!(子线程被信号唤醒);}}});handle.join().unwrap();println!();}fndemo_producer_consumer(){println!( demo_producer_consumer 生产者消费者 );letinnerArc::new((Mutex::new(Vec::new()),Condvar::new()));letmuthandlesVec::new();letc_innerArc::clone(inner);handles.push(thread::spawn(move||{let(buf_lock,cvar)*c_inner;letmutguardbuf_lock.lock().unwrap();loop{whileguard.is_empty(){println!(消费者队列为空等待生产);guardcvar.wait(guard).unwrap();}letvalguard.remove(0);println!(消费者取出 {},val);ifval99{println!(消费者收到结束标记退出);break;}}}));letp_innerArc::clone(inner);handles.push(thread::spawn(move||{let(buf_lock,cvar)*p_inner;fornumin[10,20,30,99]{letmutgbuf_lock.lock().unwrap();g.push(num);println!(生产者放入 {},num);cvar.notify_one();drop(g);thread::sleep(Duration::from_millis(300));}}));forhinhandles{h.join().unwrap();}println!();}fnmain(){demo_basic_notify_one();demo_notify_all();demo_wait_timeout();demo_producer_consumer();println!(全部示例运行完成);}demo_basic_notify_onenotify_one 唤醒单个线程子线程条件不满足进入 wait 等待 主线程修改条件执行notify_one()子线程收到唤醒信号条件为true继续执行demo_notify_allnotify_all 广播唤醒全部等待线程线程0等待唤醒 线程1等待唤醒 线程2等待唤醒 主线程设置条件truenotify_all()线程1被 notify_all 唤醒 线程2被 notify_all 唤醒 线程0被 notify_all 唤醒demo_wait_timeout带超时等待两种结果唤醒/超时子线程wait_timeout 超时返回demo_producer_consumer 生产者消费者消费者队列为空等待生产 生产者放入10消费者取出10消费者队列为空等待生产 生产者放入20消费者取出20消费者队列为空等待生产 生产者放入30消费者取出30消费者队列为空等待生产 生产者放入99消费者取出99消费者收到结束标记退出 全部示例运行完成PSE:\leaning\slint\test

相关推荐

Rust std::sync::Barrier 栅栏详解
Rust std::sync::Barrier 栅栏详解

Rust std::sync::Barrier 栅栏详解1、 引言2、 Barrier 是什么2.1、 核心概念2.2 、与其它同步原语的区别3、Barrier 的基本用法3.1、 创建 Barrier3.2、 等待到达4、 wait() 的返回值5、 Barrier 的复用6、 实战示例:并行计算求和7、注意事项与常见陷阱7.1、 参与者… · 2026/9/27 11:30:22

嵌入式分享#47:为什么 eDP 屏会概率性不显示?(RK3568)
嵌入式分享#47:为什么 eDP 屏会概率性不显示?(RK3568)

嵌入式分#47:为什么 eDP 屏会概率性不示?(RK3568)摘要:RK3568 调试 eDP 时,屏幕发无法点亮,日显示 Link Training 失败。文分析 eDP Training 的作用并给出在 U-Boot 阶段重复训练的临时修复方案… · 2026/9/27 11:29:55

数字物业安全监测系统详解:从传感器接入到云平台告警闭环
数字物业安全监测系统详解:从传感器接入到云平台告警闭环

凌晨两点,地下车库积水越过警戒线,水泵却因为控制柜接触器卡死没有启动,值班室电话被打到爆——搞过物业弱电的人应该都见过这种场景。传统靠人工巡检、电话上报的模式,在夜间和恶劣天气下几乎等于裸奔。要系统性地解决这类问题&a… · 2026/9/27 11:29:31

毕业设计网站开发流程详细步骤
毕业设计网站开发流程详细步骤

新手入门看这篇:毕业设计网站开发全流程与避坑指南 找建站公司怕被坑高价?别慌。很多大学生和新手入门开发者在准备毕业设计时,第一反应就是找外包或者买源码,结果花了冤枉钱,代码还跑不起来。其实,只要理清思路,自己动手或者精准外包,成本能降一大半… · 2026/9/27 14:36:30

把 Claude Code 当同事委派,而不是当脚本遥控:TaoToken 统一 Key 下的 CLAUDE.md 与 subagents 配置骨架
把 Claude Code 当同事委派,而不是当脚本遥控:TaoToken 统一 Key 下的 CLAUDE.md 与 subagents 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 14:36:24

Trae AI IDEA安装与使用:TaoToken统一Key接入与settings.json配置骨架
Trae AI IDEA安装与使用:TaoToken统一Key接入与settings.json配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 14:36:18

做网站会员登陆图解步骤:3步搞定高转化入口
做网站会员登陆图解步骤:3步搞定高转化入口

做网站会员登陆图解步骤:3步搞定高转化入口 别再盯着那些千篇一律的模板网站看了。说实话,大多数企业官网的会员登录入口,要么藏在页脚角落没人点,要么设计得像个二维码广告,丑得让人不想点。很多老板觉得,网站嘛,能看就行,会员系统那是大公司才玩的… · 2026/9/27 14:36:18

网站开发如何收费?拆透建站报价单里的猫腻
网站开发如何收费?拆透建站报价单里的猫腻

网站开发如何收费?拆透建站报价单里的猫腻 域名服务器搞不懂,是不是让你在看建站报价单时心里直打鼓?别急,这行水太深,很多小白刚接触就被各种名词绕晕,生怕被坑。今天咱就掰开了揉碎了,把【网站开发如何收费】这件事讲得明明白白,让你拿到【建站报价… · 2026/9/27 14:36:06

2025年8月国产大模型综合评测:5大维度TOP10榜单与TaoToken接入配置指南
2025年8月国产大模型综合评测:5大维度TOP10榜单与TaoToken接入配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 14:36:06

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码