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

相关推荐
韩立学长1 小时前
【开题答辩实录分享】以《智能大学宿舍管理系统的设计与实现》为例进行选题答辩实录分享
数据库·spring boot·后端
编码者卢布4 小时前
【Azure Storage Account】Azure Table Storage 跨区批量迁移方案
后端·python·flask
她说..6 小时前
策略模式+工厂模式实现审批流(面试问答版)
java·后端·spring·面试·springboot·策略模式·javaee
梦梦代码精7 小时前
开源、免费、可商用:BuildingAI一站式体验报告
开发语言·前端·数据结构·人工智能·后端·开源·知识图谱
李慕婉学姐8 小时前
【开题答辩过程】以《基于Spring Boot的疗养院理疗管理系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring boot·后端
tb_first8 小时前
SSM速通2
java·javascript·后端
一路向北⁢8 小时前
Spring Boot 3 整合 SSE (Server-Sent Events) 企业级最佳实践(一)
java·spring boot·后端·sse·通信
风象南8 小时前
JFR:Spring Boot 应用的性能诊断利器
java·spring boot·后端
爱吃山竹的大肚肚8 小时前
微服务间通过Feign传输文件,处理MultipartFile类型
java·spring boot·后端·spring cloud·微服务
毕设源码-邱学长10 小时前
【开题答辩全过程】以 基于Springboot的酒店住宿信息管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端