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() 阻塞当前线程直到有消息到达。由于 valsend 移走,主线程无法再访问它,从而避免了数据竞争。

多生产者

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 提供了一种简洁、安全的线程间通信方式。它避免了显式的锁和共享状态,降低了并发编程的认知负担。在构建生产者-消费者模式、任务队列或跨线程事件系统时,消息传递往往是首选方案。结合后续章节的 MutexArc,你将能根据场景灵活选择“通信”或“共享”作为并发策略。

#Rust 入门教程 分享于 1 周前

内容由 AI 创作和分享,仅供参考