Android程序员初学Rust-通道

Rust 通道由两部分组成:Sender<T> 和 Receiver<T>。这两部分通过通道相互连接,但你只能看到端点:

Rust 复制代码
use std::sync::mpsc;

fn main() {
    let (tx, rx) = mpsc::channel();

    tx.send(10).unwrap();
    tx.send(20).unwrap();

    println!("Received: {:?}", rx.recv());
    println!("Received: {:?}", rx.recv());

    let tx2 = tx.clone();
    tx2.send(30).unwrap();
    println!("Received: {:?}", rx.recv());
}

// Output
// Received: Ok(10)
// Received: Ok(20)
// Received: Ok(30)

mpsc 代表多生产者、单消费者(Multi-Producer, Single-Consumer),想象一下我们的电脑屏幕,很多设备信号都可以放到屏幕上显示,但是屏幕只有一个。Sender 和 SyncSender 实现了 Clone(这样你就可以创建多个生产者),但 Receiver 没有实现。

send() 和 recv() 方法返回 Result。如果返回 Err,这意味着对应的 Sender 或 Receiver 已被丢弃,并且通道已关闭。

通过 mpsc::channel(),你可以创建一个无界且异步的通道(unbounded channel):

Rust 复制代码
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let thread_id = thread::current().id();
        for i in 0..10 {
            tx.send(format!("Message {i}")).unwrap();
            println!("{thread_id:?}: sent Message {i}");
        }
        println!("{thread_id:?}: done");
    });
    thread::sleep(Duration::from_millis(100));

    for msg in rx.iter() {
        println!("Main: got {msg}");
    }
}

输出如下:

makefile 复制代码
// Output
ThreadId(2): sent Message 0
ThreadId(2): sent Message 1
ThreadId(2): sent Message 2
ThreadId(2): sent Message 3
ThreadId(2): sent Message 4
ThreadId(2): sent Message 5
ThreadId(2): sent Message 6
ThreadId(2): sent Message 7
ThreadId(2): sent Message 8
ThreadId(2): sent Message 9
ThreadId(2): done
Main: got Message 0
Main: got Message 1
Main: got Message 2
Main: got Message 3
Main: got Message 4
Main: got Message 5
Main: got Message 6
Main: got Message 7
Main: got Message 8
Main: got Message 9

如果要在多线程的情况下发送消息呢?

Rust 复制代码
fn main() {
    let (tx, rx) = mpsc::channel();
    
    // 注意这里的写法,多使用了一对大括号,在最后返回的闭包。
    thread::spawn({
        let tx = tx.clone();
        move || {
            for i in 0..10 {
                tx.send(i).unwrap();
            }
        }
    });

    thread::spawn(move || {
        for i in 10..20 {
            tx.send(i).unwrap();
        }
    });

    for i in rx {
        println!("received {}", i);
    }
}

当 tx 的所有权被释放的时候,通道也就关闭了。所以,你可以尝试一下,如果在第二个线程的地方,也使用类似克隆的方式,你会发现,最后这个 for 循环,会一直阻塞。

一个无界通道会根据存储待处理消息的需要分配足够的空间。send() 方法不会阻塞调用线程。

如果通道已关闭,调用 send() 方法将终止并返回一个错误(这就是它返回 Result 的原因)。当接收器被释放时,通道就会关闭。

对于有界(同步)通道(bounded channel),send() 可能会阻塞当前线程:

Rust 复制代码
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::sync_channel(3);

    thread::spawn(move || {
        let thread_id = thread::current().id();
        for i in 0..10 {
            tx.send(format!("Message {i}")).unwrap();
            println!("{thread_id:?}: sent Message {i}");
        }
        println!("{thread_id:?}: done");
    });
    thread::sleep(Duration::from_millis(100));

    for msg in rx.iter() {
        println!("Main: got {msg}");
    }
}

调用 send() 会阻塞当前线程,直到通道中有空间来存放新消息。如果没有人从通道中读取消息,线程可能会无限期阻塞。

与无界通道一样,如果通道已关闭,调用 send() 会因错误而中止。

大小为零的有界通道称为"会合通道"。每次调用 send() 都会阻塞当前线程,直到另一个线程调用 recv()。

相关推荐
子兮曰1 小时前
把 649 种文件格式塞进一个网页里:一个「文件不出浏览器」的预览器
前端·javascript·后端
全栈弄潮儿4 小时前
Python实战第2期: 变量与数据类型
后端·python·agent
码事漫谈5 小时前
五道题,测测你有没有真的入门 AI
后端
蜗牛互联网5 小时前
Python + JSON Schema 实现工单结构化输出与本地复核
java·人工智能·后端
DongQiShanRen6 小时前
玄龙(上):TICK 主循环——意识心跳怎么跳
linux·jvm·数据库·人工智能·数据挖掘·rust
编程序的员6 小时前
使用OpenTelemetry来监控Rust应用
运维·rust·monitoring·opentelemetry
大侠归来7 小时前
Spring Boot 2.1 → 3.5 迁移推演:从实战出发的完整路线图
java·spring boot·后端
海马7 小时前
Spring Boot 开发知识整理
java·spring boot·后端
谢亮_vipxieliang7 小时前
Spring Boot 3.x 从零开始——环境搭建与第一个 REST 项目
java·spring boot·后端