通道(1):消息所有权、多生产者与有界通道
通过发送与接收组织线程协作,理解通道结束条件和容量带来的等待。
学习日期:2026-10-14。本篇与同日的「通道」其他篇章合计为一个模块、一天学习量。
文中的完整代码保留当前示例的有效语句,省去原文件中的注释与已注释实验行;实验变体另行说明。运行命令在项目根目录执行,示例名称由 Cargo.toml 注册。
mpsc::channel:通过消息转移所有权
mpsc::channel::<String>() 创建发送端和接收端。子线程将 String 传给 send,接收方通过 recv() 得到消息的所有权,发送方不能继续使用已移动的字符串。
recv() 会在暂时没有消息时等待。所有发送端释放且缓冲区没有剩余消息时,接收才返回断开错误。
use std::sync::mpsc;use std::thread;
fn main() { let (sender, reciver) = mpsc::channel::<String>();
let handle = thread::spawn(move || { let message = String::from("hello");
sender.send(message).unwrap();
println!("子线程发送完成"); });
match reciver.recv() { Ok(message) => println!("收到: {}", message), Err(error) => println!("接收失败: {}", error), }
handle.join().unwrap();}运行:
cargo run --example channel_basic本次本地运行的标准输出如下。并发日志只代表这次运行,先后关系以正文说明为准。
子线程发送完成收到: hello理解与实验
发送完成的日志和接收日志先后不固定。普通无界通道发送成功,不代表消费者已经处理完消息,也不替代等待线程退出。
示例中变量 reciver 只是接收端的变量名。显式类型参数也有实验价值:删除发送语句后,通道仍知道消息类型,不会先遇到类型无法推断的问题。
多个生产者:结束条件包含所有发送端
克隆发送端,让两个线程往同一个接收端发送消息。这里每个生产者只发送一条,不同线程之间的接收顺序没有固定保证。
for message in receiver 会不断接收,直到所有发送端消失且剩余消息处理完。本例两个发送端分别随子线程结束而释放,因此循环能够退出。
use std::sync::mpsc;use std::thread;
fn main() { let (sender, receiver) = mpsc::channel(); let second_sender = sender.clone();
let first = thread::spawn(move || { sender.send(String::from("来自线程一")).unwrap(); });
let second = thread::spawn(move || { second_sender.send(String::from("来自线程二")).unwrap(); });
for message in receiver { println!("收到: {}", message); }
first.join().unwrap(); second.join().unwrap();
println!("接收结束");}运行:
cargo run --example channel_multi本次本地运行的标准输出如下。并发日志只代表这次运行,先后关系以正文说明为准。
收到: 来自线程一收到: 来自线程二接收结束理解与实验
如果在主线程保留 spare_sender,即使它从不发送消息,接收循环仍然会等待,因为还有发送端存活。应在进入循环前释放不再使用的发送端。
mpsc 的含义是多个生产者、单个消费者;不要把发送端可以克隆理解为同一个标准库接收端也可任意克隆。
sync_channel:容量限制与背压
容量为 1 时,通道最多缓存一条待接收消息。缓存满时,后续 send() 会阻塞,直到有空位,或者接收端断开。
背压让生产者感知消费者的处理节奏。不过“第二条发送完成”和接收日志的先后仍不固定,因为日志语句与消息交接不是同一个操作。
use std::sync::mpsc;use std::thread;
fn main() { let (sender, reciver) = mpsc::sync_channel(1);
let handle = thread::spawn(move || { sender.send("第一条").unwrap(); println!("第一条发送完成");
sender.send("第二条").unwrap(); println!("第二条发送完成"); });
for message in reciver { println!("收到: {}", message); }
handle.join().unwrap();}运行:
cargo run --example channel_bounded本次本地运行的标准输出如下。并发日志只代表这次运行,先后关系以正文说明为准。
第一条发送完成收到: 第一条收到: 第二条第二条发送完成理解与实验
容量 0 也合法,表示不缓存:发送与接收必须直接交接,send 才能成功返回。
本例先接收,再 join()。若主线程先等待生产者退出,生产者又因缓冲已满而等待主线程接收,就可能互相等待形成死锁;通道不会自动将这种等待转换成 panic。
同模块笔记
标准库参考
Rust 学习笔记
连载中分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 1 天
部分内容可能已过时