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),想象一下我们的电脑屏幕,很多设备信号都可以放到屏幕上显示,但是屏幕只有一个。SenderSyncSender 实现了 Clone(这样你就可以创建多个生产者),但 Receiver 没有实现。

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

通过 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()

相关推荐
元界metalite1 小时前
SpringBoot整合RocketMQ-毒丸消息还要无限重试吗
后端
SimonKing2 小时前
升级Spring Boot 4后,从 Jackson 2 到 3,到底有哪些变化
java·后端·程序员
YIAN2 小时前
Docker + Nginx 核心原理扫盲:从环境隔离到反向代理,运维面试必考点
后端·docker·面试
万物智能2 小时前
启动链路与分区—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
后端·架构
寒蝉1282 小时前
一个简单操作是怎么在分布式环境下变复杂的
后端
苏三的开发日记2 小时前
Windows宿主机+VMware CentOS虚拟机 + 同一个Wi-Fi下的其他实体电脑,三者可以互相访问
后端
boooooooom2 小时前
手把手做一个图 RAG 烹饪问答系统:Neo4j + Milvus + LLM 的工程实践
前端·javascript·后端
newerp2 小时前
Golang 切片底层结构
后端·程序员·go
BingoGo2 小时前
免费可商用 PHP 管理后台 CatchAdmin V5.4.0 发布,新增短信服务能力
后端·php
Csvn2 小时前
🐍 Day 8:面向对象编程
后端·python