10.2 消息传递:使用 mpsc 通道
听
Rust 鼓励“通过通信共享内存,而非通过共享内存来通信”的并发模型。这一理念源自 Go 语言的格言,在 Rust 中通过 mpsc(multi-producer, single-consumer)通道得以实现。std::sync::mpsc 模块提供了一种安全、高效且符合所有权规则的线程间通信机制。
基本用法
调用 mpsc::channel() 会返回一个元组 (sender, receiver),分别用于发送和接收消息:
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hello");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Received: {}", received);
}
send() 将值移入通道,recv() 阻塞当前线程直到有消息到达。由于 val 被 send 移走,主线程无法再访问它,从而避免了数据竞争。
多生产者
mpsc 支持多个发送端。调用 tx.clone() 可创建新的 Sender:
let (tx, rx) = mpsc::channel();
for i in 0..3 {
let tx_clone = tx.clone();
thread::spawn(move || {
tx_clone.send(format!("Message from thread {}", i)).unwrap();
});
}
// 显式丢弃原始 tx,避免 recv 阻塞等待更多消息
drop(tx);
for _ in 0..3 {
println!("{}", rx.recv().unwrap());
}
注意:只有当所有 Sender 实例都被销毁后,recv() 才会在通道为空时返回 Err。因此,在确定不再发送消息后,应显式 drop(tx) 或让其离开作用域。
非阻塞接收:try_recv
若不希望阻塞,可使用 try_recv():
match rx.try_recv() {
Ok(msg) => println!("Got: {}", msg),
Err(_) => println!("No message available"),
}
但通常更推荐在需要异步行为时使用 async/await(见 10.5 节),而非轮询。
发送任意 Send 类型
只要类型实现了 Send trait(几乎所有标准类型都满足),就可以通过通道传递,包括复杂结构体、Box<T>、甚至其他通道的发送端:
#[derive(Debug)]
struct Job {
id: u32,
payload: String,
}
let (tx, rx) = mpsc::channel();
tx.send(Job { id: 1, payload: "work".into() }).unwrap();
与所有权系统的集成
通道天然契合 Rust 的所有权模型:
send()获取值的所有权,发送后原变量不可用;- 接收端获得完整所有权,无需额外同步;
- 编译器确保不会同时存在对同一数据的多个可变引用。
小结
mpsc 通道为 Rust 提供了一种简洁、安全的线程间通信方式。它避免了显式的锁和共享状态,降低了并发编程的认知负担。在构建生产者-消费者模式、任务队列或跨线程事件系统时,消息传递往往是首选方案。结合后续章节的 Mutex 和 Arc,你将能根据场景灵活选择“通信”或“共享”作为并发策略。
#Rust 入门教程
分享于 1 周前