channel 消息传递
你会学到什么
mpsc::channel创建发送端Sender和接收端Receiver。- “不要用共享内存来通信,要用通信来共享内存”。
- 接收端可以像迭代器一样消费,直到所有发送端关闭。
最小示例
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
tx.send(42).unwrap();
assert_eq!(rx.recv().unwrap(), 42);
运行代码
cd examples
cargo run -p rt_26_channels
cargo test -p rt_26_channels
代码讲解
生产者线程持有 Sender,主线程从 Receiver 收集:
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
for value in 1..=count {
sender.send(value * value).unwrap();
}
// sender drop -> 接收端迭代结束
});
let result: Vec<i32> = receiver.iter().collect();
receiver.iter() 会阻塞等待,直到所有 Sender 都被 drop 才结束。mpsc 意为 multi-producer single-consumer——可以 clone 多个发送端,但只有一个接收端。
常见错误
接收端一直阻塞,因为发送端没被 drop:
let (tx, rx) = mpsc::channel();
// 保留了 tx 又去 rx.iter().collect() -> 永远等待
确保发送完后让 Sender 离开作用域。
练习
- 用
tx.clone()创建两个生产者线程,主线程汇总它们的消息。 - 用 channel 实现一个简单的任务队列。
小结
channel 把数据所有权从一个线程“搬运”到另一个,天然避免数据竞争,是 Rust 推荐的线程通信方式。
下一步
有时确实需要共享可变状态。下一章学习 Arc<Mutex<T>>。
完整示例代码
下面是 examples/26_channels/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。
examples/26_channels/src/main.rs
//! 用 mpsc channel 在线程间传递消息。
use std::sync::mpsc;
use std::thread;
/// 生产者线程发送平方数,主线程收集。
fn squares(count: i32) -> Vec<i32> {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
for value in 1..=count {
sender.send(value * value).expect("接收端已关闭");
}
// sender 在此被 drop,接收端的迭代随之结束。
});
// rx 的迭代器会一直收到发送端关闭为止。
receiver.iter().collect()
}
fn main() {
println!("squares = {:?}", squares(5));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collects_messages() {
assert_eq!(squares(4), vec![1, 4, 9, 16]);
}
#[test]
fn zero_produces_nothing() {
assert_eq!(squares(0), Vec::<i32>::new());
}
} examples/26_channels/Cargo.toml
[package]
name = "rt_26_channels"
version.workspace = true
edition.workspace = true
publish.workspace = true