【Rust自学】16.2. 使用消息传递来跨线程传递数据
16.2 使用消息传递来跨线程传递数据16.2.1. 消息传递有一种很流行而且能保证安全并发的技术叫做消息传递。在这种机制里线程或 Actor通过彼此间发送消息数据来进行通讯。Go 语言有一句名言是这么说的Do not communicate by sharing memory; instead, share memory by communicating.不要用共享内存来通信要用通信来共享内存Go 语言的并发模型体现了这种思想。Rust 也提供了基于消息传递的一种并发方式具体就是使用标准库中的Channel。Go 语言里也有Channel思路差不多。16.2.2. 理解Channel可以将编程中的Channel想象为定向水道例如小溪或河流。如果你把橡皮鸭之类的东西放入河中它会顺流而下到达水道的尽头。通道有两部分发送端和接收端。发送端是将橡皮鸭放入河中的上游位置接收端是橡皮鸭最终到达下游的位置。代码的一部分使用要发送的数据调用发送端上的方法另一部分检查接收端是否有到达的消息。如果发送端或接收端其一消失则称通道已关闭。具体的步骤- 调用发送端的方法发送数据- 接收端会检查和接收到达的数据- 如果发送端、接收端中的任意一端被丢弃了那么Channel就关闭了。16.2.3. 创建channel使用mpsc::channel函数来创建Channel。mpsc表示 multiple producer, single consumer多个生产者、一个消费者表示可以有多个发送端但是只能有一个接收端。调用这个函数返回一个元组有两个元素分别是发送端和接收端。看个例子use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) mpsc::channel(); thread::spawn(move || { let val String::from(hi); tx.send(val).unwrap(); }); let received rx.recv().unwrap(); println!(Got: {received}); }首先使用mpsc::channel函数来创建Channel返回的元组使用模式匹配进行解构分别用tx和rx表示发送端和接收端。接下来创建了一个线程使用move关键字表示发送端tx的所有权被移至分线程内因为线程必须拥有通道发送端的所有权才能往通道里发消息。使用send方法来发送消息返回类型是Result类型如果接收端被丢弃了那么返回值就是Err反之就是Ok。在这里面就简单地使用unwrap进行错误处理即可这样如果接收端被丢弃就会恐慌。接收端有两个方法来获取消息这里使用了recv方法receive的简写。它会一直阻塞这个线程直到有消息被传入为止。消息被包裹在Result类型中有消息就返回Ok反之就是Err一样使用unwrap简单地处理错误即可。输出Got: hi发送端的send方法send方法的参数是想要发送的数据返回Result类型。如果有问题例如接收端已经被丢弃就会返回Err。接收端的方法recv方法阻止当前线程执行直到Channel中有值传来一旦收到值就返回Result类型如果发送端关闭了就会收到Err。try_recv方法不会阻塞当前线程执行立即返回Result类型有数据到达就是Ok变体包裹着传过来的数据否则就返回错误。通常是使用循环调用来检查try_recv的结果。一旦有消息来了就开始处理如果没来那么这时候也可以执行其他指令。16.2.4.channel和所有权转移所有权在消息传递中非常重要它能帮你编写安全、并发的代码。看个例子use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) mpsc::channel(); thread::spawn(move || { let val String::from(hi); tx.send(val).unwrap(); println!(val is {val}); }); let received rx.recv().unwrap(); println!(Got: {received}); }在刚才的代码上加了println!(val is {val});这句话。把值传入send函数后想继续在线程里使用值。输出$ cargo run Compiling message-passing v0.1.0 (/tmp/projects/message-passing) error[E0382]: borrow of moved value: val -- src/main.rs:10:27 | 8 | let val String::from(hi); | --- move occurs because val has type String, which does not implement the Copy trait 9 | tx.send(val).unwrap(); | --- value moved here 10 | println!(val is {val}); | ^^^ value borrowed here after move For more information about this error, try rustc --explain E0382. error: could not compile message-passing (bin message-passing) due to 1 previous error错误在于借用了已经移动的值val。它的所有权已经在传入send时移交出去了所以就会报错。下一个例子通过发送多个值来观察接收者等待的过程use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) mpsc::channel(); thread::spawn(move || { let vals vec![ String::from(hi), String::from(from), String::from(the), String::from(thread), ]; for val in vals { tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); for received in rx { println!(Got: {received}); } }分线程以循环的方式发送Vector里的各个元素每次发送完之后会暂停 1 秒主线程把接收端当作一个迭代器来使用因为实现了Iteratortrait这样就不需要显式调用recv函数了。每收到一个值就将它打印出来。当发送端执行完毕被丢弃时Channel就关闭了循环就不会继续。程序退出。输出Got: hi Got: from Got: the Got: thread16.2.5. 通过克隆创建多个发送者继续在上一个代码的基础上稍作修改use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) mpsc::channel(); let tx1 tx.clone(); thread::spawn(move || { let vals vec![ String::from(hi), String::from(from), String::from(the), String::from(thread), ]; for val in vals { tx1.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); thread::spawn(move || { let vals vec![ String::from(more), String::from(messages), String::from(for), String::from(you), ]; for val in vals { tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); for received in rx { println!(Got: {received}); } }这里多了一个分线程现在有 2 个分线程都想要给主线程发消息所以就需要两个发送端。针对这种情况只需要对代表发送端的变量tx使用clone方法即可也就是原文的let tx1 tx.clone();这一句。输出接收顺序不确定以下为一次代表性的本地运行结果Got: hi Got: more Got: from Got: messages Got: the Got: for Got: thread Got: you接收端收到的数据会交错来自两个发送端。

相关新闻

[GESP202606 七级] 染色

[GESP202606 七级] 染色

P17014 [GESP202606 七级] 染色 https://www.luogu.com.cn/problem/P17014 GESP7级终于通过啦!! https://www.bilibili.com/video/BV1kKXWYrExs/ GESP七级C历年真题讲解 https://www.bilibili.com/video/BV1Fk2wBxEHm/ 【6月GESP考后大盘点:七…

2026/10/20 17:35:24 阅读更多 →
试了一下 qData 开源版:更像是给数据中台做一次“低成本试跑”

试了一下 qData 开源版:更像是给数据中台做一次“低成本试跑”

最近在看数据中台相关产品,顺手体验了一下 qData 开源版。先说结论:它给我的感觉不是那种只能看概念、看宣传页的产品,而是更适合技术团队拿来做一次数据中台的入门验证。尤其是对于还没正式立项、但已经开始感觉数据管理有点乱的团队来说&am…

2026/10/19 21:27:26 阅读更多 →
计算机毕业设计之在线选课系统

计算机毕业设计之在线选课系统

随着信息化时代的到来,系统管理都趋向于智能化、系统化,在线选课系统也不例外,但目前国内的有些学校仍然都使用人工管理,学校规模越来越大,同时信息量也越来越庞大,人工管理显然已无法应对时代的变化&#…

2026/10/20 9:36:52 阅读更多 →

最新新闻

基于语义向量的名著推荐系统:从协同过滤到Django工程实践

基于语义向量的名著推荐系统:从协同过滤到Django工程实践

1. 为什么经典名著做推荐,比想象中难得多我做在线阅读平台的项目已经有几年了,从资讯流到工具类产品都碰过,但真正让我觉得"推荐系统不是套个协同过滤就能完事"的,恰恰是这个古典名著推荐项目。以前做过的商品推荐、视频…

2026/10/20 17:55:50 阅读更多 →
WorkBuddy链接微信实战:用Webhook将消息流改造成自动化工作流

WorkBuddy链接微信实战:用Webhook将消息流改造成自动化工作流

这个系列写到第七篇,前面几篇已经把 WorkBuddy 的安装部署、任务编排、数据看板这些底层能力聊了不少。但说实话,后台私信里问得最多的,反而是“我的微信能不能接进去”。这其实不奇怪——大部分人日常最高频的信息入口就是微信,工…

2026/10/20 17:55:50 阅读更多 →
小熊派nano环境搭建实战:交叉编译与固件烧录全流程

小熊派nano环境搭建实战:交叉编译与固件烧录全流程

第一次拿到小熊派nano这块板子,很多人的第一反应是先找个教程点个灯。但真走到“点灯”这一步,中间隔着的不是一根杜邦线,而是一整条工具链:电脑上得装对应的交叉编译器,得把官方源码仓库完整克隆到本地,还…

2026/10/20 17:55:49 阅读更多 →
无ESDF的无人机局部规划器EGO-Planner:B样条轨迹优化解析

无ESDF的无人机局部规划器EGO-Planner:B样条轨迹优化解析

1. 这篇论文到底解决了什么问题1.1 传统ESDF为什么会让局部规划器“变重”ESDF是Euclidean Signed Distance Field的缩写,每个体素里存的是它到最近障碍物表面的有符号距离。对于轨迹优化来说,这个东西太好用了:只要查询某个空间点&#xff0…

2026/10/20 17:55:49 阅读更多 →
AI数据安全实战:从投毒、注入到泄露的防御与排查

AI数据安全实战:从投毒、注入到泄露的防御与排查

聊AI人工智能领域的数据安全,很多人的第一反应是权限、加密、备份。但在真实的大模型项目里,这些传统手段往往只是基础,数据泄露真正让人头疼的地方在于:数据一旦进入模型,就很难被"删掉"。模型会记住某些训…

2026/10/20 17:55:49 阅读更多 →
大模型入门必读:从深度学习原理到工程实战的系统书单

大模型入门必读:从深度学习原理到工程实战的系统书单

最近这半年,总有人在后台问我:“我想入门大模型,该看什么书?”老实说,每次被问到这个问题,我都挺为难的。现在网上关于大模型的书单五花八门,有讲算法的、有讲应用的、有讲产业趋势的&#xff0…

2026/10/20 17:54:48 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/20 9:50:45 阅读更多 →