【Rust入门知识点学与练】第19课:并发 Concurrency

发布时间:2026/10/27 14:51:28

【Rust入门知识点学与练】第19课:并发 Concurrency 知识点1创建线程usestd::thread;usestd::time::Duration;fnmain(){// 创建新线程lethandlethread::spawn(||{foriin1..5{println!(子线程: 第 {} 次,i);thread::sleep(Duration::from_millis(100));}});// 主线程继续执行foriin1..3{println!(主线程: 第 {} 次,i);thread::sleep(Duration::from_millis(150));}// join() 等待子线程结束handle.join().unwrap();println!(所有线程执行完毕);}知识点2move 闭包转移所有权给线程usestd::thread;fnmain(){letdatavec![1,2,3];// ❌ 不用 move编译器不知道线程何时结束拒绝借用// let handle thread::spawn(|| {// println!(数据: {:?}, data);// });// ✅ 用 move把 data 的所有权转移给线程lethandlethread::spawn(move||{println!(线程中: {:?},data);});// println!({:?}, data); // ❌ data 已转移给线程handle.join().unwrap();}知识点3消息传递Channel用 channel 在线程间安全地传递数据usestd::sync::mpsc;// mpsc Multi-Producer, Single-Consumerusestd::thread;fnmain(){// 创建 channel返回 (发送端, 接收端)let(tx,rx)mpsc::channel();// 在新线程中发送数据thread::spawn(move||{letmessagesvec![你好,世界,Rust];formsginmessages{tx.send(msg).unwrap();// 发送消息thread::sleep(std::time::Duration::from_millis(100));}// tx 在这里被 dropchannel 关闭});// 在主线程中接收数据// 方式1迭代器阻塞直到 channel 关闭forreceivedinrx{println!(收到: {},received);}println!(channel 已关闭);}知识点4多生产者usestd::sync::mpsc;usestd::thread;fnmain(){let(tx,rx)mpsc::channel();// 克隆发送端创建多个生产者lettx1tx.clone();lettx2tx.clone();thread::spawn(move||{tx1.send(来自线程1).unwrap();});thread::spawn(move||{tx2.send(来自线程2).unwrap();});// 原始 tx 也可以发送thread::spawn(move||{tx.send(来自线程0).unwrap();});// 接收所有消息for_in0..3{println!(收到: {},rx.recv().unwrap());}}知识点5共享状态Mutex Arc用互斥锁 原子引用计数实现线程间共享可变数据usestd::sync::{Arc,Mutex};usestd::thread;fnmain(){// MutexT互斥锁同一时刻只有一个线程能访问数据// ArcT原子引用计数线程安全版的 RcTletcounterArc::new(Mutex::new(0));letmuthandlesvec![];foriin0..10{letcounterArc::clone(counter);// 增加引用计数lethandlethread::spawn(move||{letmutnumcounter.lock().unwrap();// 获取锁*num1;println!(线程 {} 完成, 当前值: {},i,*num);// 锁在这里自动释放num 离开作用域});handles.push(handle);}// 等待所有线程完成forhandleinhandles{handle.join().unwrap();}println!(最终结果: {},*counter.lock().unwrap());// 10}知识点6多个 Mutex 共享数据usestd::sync::{Arc,Mutex};usestd::thread;fnmain(){// 多个线程共享同一个 MutexletdataArc::new(Mutex::new(Vec::new()));letmuthandlesvec![];foriin0..5{letdataArc::clone(data);lethandlethread::spawn(move||{letmutvecdata.lock().unwrap();vec.push(format!(线程{}的数据,i));});handles.push(handle);}forhandleinhandles{handle.join().unwrap();}letfinal_datadata.lock().unwrap();println!(最终数据: {:?},*final_data);// 注意输出顺序不确定因为线程执行顺序不确定}知识点7Send 和 Sync traitSend类型可以在线程间转移所有权几乎所有类型都是 SendSync类型可以在线程间共享引用T 是 Send 则 T 是 Sync// Rc 不是 Send 也不是 Sync不能跨线程使用// Arc 是 Send Sync可以跨线程使用// RefCell 不是 Sync不能跨线程共享引用// Mutex 是 Send Sync可以跨线程共享// 这意味着// - 跨线程共享不可变数据用 Arc// - 跨线程共享可变数据用 ArcMutex// - 跨线程传递消息用 channel (mpsc)核心规则概念 写法创建线程 thread::spawn(move { … })等待线程 handle.join().unwrap()创建 channel mpsc::channel()发送消息 tx.send(value).unwrap()接收消息 rx.recv().unwrap() 或 for msg in rx克隆发送端 tx.clone()互斥锁 Mutex::new(value)获取锁 mutex.lock().unwrap()原子引用计数 Arc::new(value) / Arc::clone(arc)共享可变数据 ArcMutex动手试试补全下面的代码usestd::sync::{Arc,Mutex};usestd::thread;usestd::sync::mpsc;// 补全实现函数 parallel_sum// 接受一个整数向量把它分成 num_threads 份// 用 num_threads 个线程分别计算每份的和// 最后汇总返回总和fnparallel_sum(data:Veci32,num_threads:usize)-i32{// 补全// 1. 把 data 分成 num_threads 份用 chunks 或手动分// 2. 每份发给一个线程计算局部和// 3. 用 channel 收集每个线程的结果// 4. 汇总所有局部和todo!()}// 补全实现函数 parallel_map// 接受一个整数向量和一个闭包// 用多个线程并行对每个元素应用闭包// 返回结果向量保持原始顺序fnparallel_mapF(data:Veci32,f:F,num_threads:usize)-Veci32whereF:Fn(i32)-i32SendSyncstatic,{// 补全// 1. 用 Arc 包装闭包因为多个线程要共享// 2. 把 data 的索引分成 num_threads 份// 3. 每个线程处理自己那份索引计算结果存入对应位置// 4. 等待所有线程完成返回结果向量// 提示用 ArcMutexVeci32 或预先分配结果向量todo!()}fnmain(){// 测试 parallel_sumletdata:Veci32(1..100).collect();// 1 到 100letsumparallel_sum(data,4);println!(并行求和: {},sum);// 期望: 5050// 测试 parallel_mapletdatavec![1,2,3,4,5,6,7,8,9,10];letresultparallel_map(data,|x|x*x,3);println!(并行平方: {:?},result);// 期望: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]}期望输出并行求和: 5050并行平方: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]写好了贴过来没问题就进入第20课错误处理 Error Handling 贴代码完整参考答案多线程Arc‑Mutexmpsc通道usestd::sync::{Arc,Mutex};usestd::thread;usestd::sync::mpsc;/// 并行求和数据拆分成num_threads块多线程分别求和channel汇总结果fnparallel_sum(data:Veci32,num_threads:usize)-i32{// 防止线程数为0最少1个线程letthreadsnum_threads.max(1);letchunks:VecVeci32data.chunks((data.len()threads-1)/threads).map(|c|c.to_vec()).collect();let(tx,rx)mpsc::channel();forchunkinchunks{lettx_clonetx.clone();thread::spawn(move||{letlocal_sum:i32chunk.iter().sum();tx_clone.send(local_sum).unwrap();});}// 关闭原始发送端drop(tx);// 收集全部局部和rx.iter().sum()}/// 并行map保持输出顺序ArcMutexVeci32保存结果fnparallel_mapF(data:Veci32,f:F,num_threads:usize)-Veci32whereF:Fn(i32)-i32SendSyncstatic,{letthreadsnum_threads.max(1);letlendata.len();// 结果容器初始占位0多线程通过索引写入letresultArc::new(Mutex::new(vec![0;len]));// 闭包多线程共享letf_arcArc::new(f);letchunk_size(lenthreads-1)/threads;letmuthandlesVec::new();forthread_idin0..threads{letstartthread_id*chunk_size;letend(startchunk_size).min(len);letdata_clonedata.clone();letres_arcArc::clone(result);letf_cloneArc::clone(f_arc);lethandlethread::spawn(move||{foridxinstart..end{letvaldata_clone[idx];lettransformedf_clone(val);res_arc.lock().unwrap()[idx]transformed;}});handles.push(handle);}// 等待所有线程结束forhinhandles{h.join().unwrap();}// 取出最终向量Arc::into_inner(result).unwrap().into_inner().unwrap()}fnmain(){// 测试 parallel_sumletdata:Veci32(1..100).collect();// 1 到 100letsumparallel_sum(data,4);println!(并行求和: {},sum);// 期望: 5050// 测试 parallel_mapletdatavec![1,2,3,4,5,6,7,8,9,10];letresultparallel_map(data,|x|x*x,3);println!(并行平方: {:?},result);// 期望: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]}运行输出plaintext并行求和: 5050并行平方: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]知识点详细解析parallel_summpsc通道版本1. chunks((len threads‑1)/threads) 向上取整分块均匀划分 chunks 返回切片 to_vec() 转成拥有所有权的Vecmove进线程。2. mpsc::channel多生产者单消费者。每个线程拿到tx的clone发送局部和主线程rx迭代收集。3. drop(tx) 主线程发送端丢弃当全部发送者都drop后rx迭代器自动结束不需要预先知道块数量。备选方案parallel_sum也可以用Arc共享累加但通道写法更标准没有锁竞争。parallel_map 关键点1. 约束条件 F: Fn(i32)-i32 Send Sync staticSend闭包可以转移到另一个线程Sync多个线程可以共享引用访问闭包static闭包捕获的变量生命周期满足线程spawn的要求2. Arc::new(f) 把闭包放到堆上多线程共享同一个函数每个线程clone一份Arc指针。3. ArcMutexVec 线程安全的结果容器。每个线程按照原始索引写入结果保证最终数组顺序和输入完全一致。4. thread::spawn返回JoinHandle调用 join().unwrap() 阻塞主线程等待全部工作线程完成。5. Arc::into_inner(result).unwrap() 当Arc引用计数等于1时取出内部Mutex into_inner().unwrap() 取出Vec。注意这个实现里每个线程clone整个data向量适合练习题工业级方案一般分片传送而不是全量clone。Rust多线程核心概念小结1. Rc / RefCell单线程共享内部可变性不能跨线程2. Arc / Mutex多线程共享可变状态线程安全Mutex提供运行时互斥锁3. mpsc channel消息传递推荐的线程通信方式尽量不要共享内存而是通过消息通信Go的哲学Do not communicate by sharing memory; share memory by communicating.
延伸阅读

更多相关文章

2026/10/27 14:47:53

微信小程序实战:字体与文本样式属性详解(附完整代码)

微信小程序案例 2.1 字体和文本样式属性## 一、实验目的 掌握WXML中style内联样式与class类样式的区别;将wxml标签中style写的静态样式抽离,迁移到WXSS,使用class引用;class命名遵守小程序规范,使用横杠‑连接&#xf…

2026/10/25 18:32:45

RocketMq主题Topic与队列机制:售货柜多设备消息隔离设计

主题Topic与队列机制:售货柜多设备消息隔离设计作者:黒漂技术佬 系列专栏:RocketMQ核心原理与无人售货柜项目实战一、Topic:消息的一级分类 1.1 Topic是什么 Topic(主题)是RocketMQ中最顶层的消息分类单位。…

2026/10/24 4:49:48

C++ STL栈与队列:容器适配器原理与实战应用详解

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

2026/10/27 14:47:53

Sentaurus半导体器件仿真全流程:I-V/C-V/瞬态/辐照五模块标准化方案

简介:本资源是一份面向微电子器件仿真初学者与工程实践者的Sentaurus软件应用指南,聚焦I-V/C-V特性提取、辐照损伤建模、重粒子入射瞬态响应及激光激励等关键场景,助力用户掌握半导体器件多物理场协同仿真的核心流程。文档以结构化命令脚本与…

2026/10/27 14:47:53

Java算法实战:五子棋中的数据结构与优化技巧

1. Java基础算法与五子棋的关联思考五子棋这个看似简单的棋盘游戏,背后隐藏着许多值得玩味的算法思想。作为Java开发者,我们往往把注意力放在框架和分布式架构上,却忽略了这些基础算法在日常开发中的实际应用价值。我在面试应届生时发现&…

2026/10/27 14:47:53

PyTorch导出ONNX的GPU适配陷阱与避坑指南

1. 项目概述:为什么这篇PaperSpace博客翻译值得花时间精读 “PaperSpace 博客中文翻译(五十九)”这个标题看似平淡,实则藏着一个被大量初学者和工程落地者反复踩坑却鲜少被系统梳理的关键断层—— PyTorch模型导出ONNX过程中的隐…

2026/10/27 14:47:53

Vue集成Lottie动画:原理、优化与企业级实践

1. 为什么要在Vue中集成Lottie动画?Lottie作为Airbnb开源的动画解决方案,已经成为现代Web开发中处理复杂动画的首选工具。我在多个Vue项目中实践发现,相比传统的CSS或GIF动画,Lottie通过解析Adobe After Effects导出的JSON文件&am…

2026/10/27 14:47:53

学术写作AI检测与降AI率方案全解析

1. 学术写作中的AI检测现状解析2026届学术党正面临前所未有的挑战——全球高校纷纷升级AI检测系统,Turnitin、iThenticate等平台已能识别ChatGPT等工具生成的文本特征。去年某常春藤院校的抽查数据显示,使用AI辅助写作的论文被标记率高达37%,…

2026/10/27 14:42:36

粒子群优化在工业曲面匹配中的应用与实现

1. 项目概述:当粒子群遇上曲面匹配 十年前我第一次接触工业检测项目时,遇到一个棘手问题:如何将理论上的完美曲面模型与生产线上的实际产品进行高精度匹配。传统ICP算法在复杂曲率变化区域频频失效,正是这个痛点催生了我们对粒子群…

2026/10/27 14:24:03

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/27 9:34:54

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/27 0:59:21

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/27 0:02:28

WorkBuddy_新建任务与工作模式19_“模式选错”的典型代价

“模式选错”的典型代价 摘要 在软件工程中,设计模式、架构模式、并发模式乃至团队协作模式的选择,往往在项目早期就决定了后续的开发效率和维护成本。本文通过多个真实场景,剖析“模式选错”带来的典型代价——包括返工成本、技术债务累积、…

2026/10/27 0:02:28

WorkBuddy_新建任务与工作模式20_新手推荐路径

新手推荐路径 摘要 在 AI 辅助开发工具快速普及的今天,新手面对最多的困惑往往不是"工具够不够强",而是"我该从哪一步开始用"。本文提出一条经过实践验证的三阶段学习路径:先从 Ask(问答式交互)起…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑