Rust channel
std::sync::mpsc 消息传递:发送所有权、接收阻塞、多生产者与关闭语义。
#concept
#growing
#dev
#rust
[!info] related notes
Rust channel
这个概念为什么出现
“不要通过共享内存通信,而要通过通信共享内存。”
channel 把数据的所有权发送到另一线程,降低共享可变状态。
[!abstract] 一句话理解
mpsc::channel提供发送端/接收端;send转移消息所有权;所有发送端 drop 后,接收端读完缓冲消息才结束迭代。
最小可运行示例
场景:日志收集——工作线程上报事件,主线程打印
多个 worker 发送日志字符串,主线程统一写出口。
use std::sync::mpsc;
use std::thread;
// 业务意图:多生产者上报日志,单消费者打印。
// 教学点:clone sender;recv 循环;sender drop 结束。
fn main() {
let (tx, rx) = mpsc::channel::<String>();
let mut handles = Vec::new();
for i in 0..3 {
let tx = tx.clone();
handles.push(thread::spawn(move || {
tx.send(format!("event from worker {i}"))
}));
}
drop(tx); // 丢掉原始 sender,否则 rx 不结束
for msg in rx {
println!("log={msg}");
}
for handle in handles {
handle.join().expect("worker panicked").expect("receiver closed");
}
}
建议运行:cargo run
期望:三行 worker 日志(顺序不定)。
结合场景再看三个关注点
- 消息 move 到接收方
- 所有 sender drop 且缓冲排空后 rx 迭代结束;join 另行确认生产线程正常结束
- 同步 channel 背压 vs 异步 channel 生态差异
核心概念与准确模型
Sender/Receiversend/recv/try_recv- channel 无界,send 不因缓冲满而等待;sync_channel(bound) 有界,满时 send 阻塞,bound=0 为双方 rendezvous
- 有界指队列消息数量,不是总字节数;需另限制单条大小与等待中的生产者
- 关闭:全部 sender drop
SendError
设计动机
- 所有权转移 = 清晰的数据主人
- 减少锁竞争面
- 组合流水线
边界与误区
- 忘记 drop 额外 sender 导致死等
- 无界队列内存膨胀
- 需要广播/多消费者时换生态 crate
[!warning] 常见误区:假设 mpsc 保留发送顺序跨多线程总是“全局公平” 单 sender 有序;多 sender 交织。
工程实践
- 明确消息类型与关闭协议
- 有界 channel 控背压
- 错误处理不要全 unwrap
- 复杂图用成熟库(crossbeam/tokio::sync)
本节总结
- 消息传递转移所有权
- mpsc 基础模型
- 与共享状态互补
自测题
- 为什么示例要
drop(tx)? - channel 如何帮助避免数据竞争?
参考答案
- 否则仍有 sender 存活,rx 迭代不结束。
- 非 Copy 消息按值移动使发送方不能继续使用原值;若消息内部含 Arc 等共享句柄,底层数据仍需遵守同步规则。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The Book — Message Passing | 官方书 | channel |
| std::sync::mpsc | 标准库 | API |