Rust异步编程详解

文章目录

异步编程

Rust官方提供了异步编程接口,但并未提供具体的实现,为了实现Rust异步编程,需要安装第三方实现的运行时,目前最流行的是tokio。在使用前,先添加依赖

toml 复制代码
[dependencies]
tokio = { version = "1", features = ["full"] }

[[bin]]
name = "hello"
path = "hello.rs"

然后创建hello.rs文件,以实现tokio的最简调用,示例如下。

rust 复制代码
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    let task = tokio::spawn(async {
        sleep(Duration::from_millis(300)).await;
        println!("Hello async");
    });

    for i in 0..3 {
        println!("主任务工作中... {}", i);
        std::thread::sleep(Duration::from_millis(300));
    }

    task.await.unwrap();
}
/*运行结果
>cargo run --bin hello
主任务工作中... 0
主任务工作中... 1
Hello async
主任务工作中... 2
*/

上述代码中,task是一个异步任务,其功能是等待300毫秒,然后输出Hello async。当其开启之后,并不会阻塞主任务,于是在其等待的过程中,命令行依次输出"主任务工作中",直到300毫秒已到,再执行task的打印任务。

【async】是Rust语言的关键字,可将代码块转换成异步任务。

【tokio::spawn】将调用tokio运行时执行异步任务。

【await】是异步任务中的挂起命令,表示当前任务暂停,但并不影响其他任务的运行。

#[tokio::main]】是tokio提供的过程宏,用于创建并启动运行时。正如前面所说的,Rust只是提供了一个异步接口,却并未真正实现异步功能。而有了这个过程宏的修饰,main函数将在tokio的运行时中执行,从而实现异步工作。

在程序最后,task.await用于阻塞主任务,使之直到task任务执行完毕之后再退出。如果没有这行代码,同时主任务中的for循环又有2次,那么for循环执行之后,程序直接退出,我们是看不到task的有效输出的。

异步返回值

异步任务相当于给普通函数套了一层外壳,这一过程会不可避免地把函数的返回值也打包进去,最终我们想要获取这个返回值,则相当于是一个解包的过程。假设我们准备异步运行的函数的返回值是t,类型为T,则其打包解包过程如下

  1. 被async转换为Future<T>,Future是所有异步计算必须实现的trait,相当于Rust为所有第三方异步实现提供的统一接口。
  2. 被tokio::spwan调用并运行,返回一个JoinHandle<T>类型的可等待任务句柄,这是tokio为了执行异步任务,实现的具体类型。
  3. 通过await等待计算完成,以Result<T, JoinError>形式返回t,其JoinError则是Tokio定义的错误类型。

有了Result,才可以拿到异步任务真正的返回值t就可以因此,获取异步任务返回值的最简流程如下

rust 复制代码
#[tokio::main]
async fn main() {
    let handle = tokio::spawn(async {
        42
    });

    let result: Result<i32, tokio::task::JoinError> = handle.await;
    match result {
        Ok(value) => println!("Task returned: {}", value),
        Err(e) => eprintln!("Task panicked: {}", e),
    }
}

异步通道

Tokio中提供了【mspc】用于不同任务之间的通信,其语法与标准库中的mspc相似,在创建与收发过程中,主要区别就是tokio的异步任务需要在收发结束后调用await,其示例如下。

rust 复制代码
use tokio::sync::mpsc;

#[tokio::main]
async fn main() {
    let (tx, mut rx) = mpsc::channel(32);

    // 2. 启动发送任务
    tokio::spawn(async move {
        tx.send("Hello!").await.unwrap();
    });

    // 3. 接收消息(阻塞当前任务,但不阻塞线程)
    if let Some(msg) = rx.recv().await {
        println!("接收到: {}", msg);
    }
}
/*运行结果
接收到: Hello!
*/

mspc::cahnnel(32)】用于创建一组32字节的异步收发通道,其中tx用于发送,rx用于接收。

【tx.send】是异步发送函数,由于在发送过程中,必须保证tx能够存活到任务结束,故而发送代码块中使用了move关键字,强制闭包取得tx的所有权。

【tx.recv】是异步接收函数,由于接收端在接收到数据后必然会发生变化,故而tx要用mut修饰。

相关推荐
Chester_19996 小时前
CSP202203C.计算资源调度器
开发语言·数据结构·c++·蓝桥杯
EXI-小洲7 小时前
Java 操作 Word:字符串替换、图片插入、动态生成表格与API接口下载
java·开发语言·spring boot·word
会周易的程序员7 小时前
给 PLC 写一个字节码虚拟机:STVM 虚拟机架构设计
开发语言·c++·虚拟机·软plc·iec61131·stvm
机器视觉知识推荐、就业指导7 小时前
为什么老说“纯 Qt 没啥就业市场”?
开发语言·qt
telepan8 小时前
Qt 开发避坑与性能指南:如何优雅、安全地定义全局常量字符串?
开发语言·qt
m0_695251498 小时前
Qt MaintenanceTool 使用国内镜像加速下载(超详细教程)
开发语言·qt
q平面人8 小时前
【总工笔记】mindoc平板、桌面端笔记软件,发布到mindoc服务端
rust·tauri·华为平板·mindoc·总工笔记
大衛說9 小时前
《Python 从入门到精通》系列总览与学习路线
开发语言·python·学习
MC皮蛋侠客10 小时前
Tauri 2.x 系列(九):安全模型——Capabilities、Permissions、Scope 与 CSP
rust·tauri
可乐鸡翅yeah_10 小时前
hls.js 内存泄漏实战排查,直播 M3U8 长时间播放异常定位方案
开发语言·javascript·python·django·ecmascript·m3u8·m3u8在线