Rust - async/await

Rust 的 async/await 语法是编写异步代码的核心工具,它允许我们用类似同步代码的风格编写高性能的非阻塞代码。


1. 核心概念

  • 异步编程:在等待 I/O 操作(网络、文件读写等)时让出 CPU,避免阻塞线程。
  • Future trait :所有异步操作的基础,代表一个可能尚未完成的计算(类似 JavaScript 的 Promise)。
  • 执行器 (Executor) :调度和执行异步任务(如 tokioasync-std)。

2. 基本语法

(1) 定义异步函数

rust 复制代码
// 使用 `async fn` 定义异步函数
async fn fetch_data() -> String {
    // 模拟异步操作(如网络请求)
    "Hello, async!".to_string()
}
  • 返回值:async fn 返回一个实现了 Future<Output = String> 的类型(而非直接返回 String)。

(2) 等待异步结果:.await

rust 复制代码
async fn process() {
    // 使用 .await 等待异步操作完成(不阻塞线程)
    let data = fetch_data().await;
    println!("{}", data); // 输出: "Hello, async!"
}
  • .await 只能在 async 函数/块中使用。
  • 执行到 .await 时,若 Future 未完成,会让出控制权给执行器。

3. 异步代码的执行

步骤 1:创建异步运行时

rust 复制代码
#[tokio::main] // 使用 tokio 运行时
async fn main() {
    process().await;
}
  • 需要第三方运行时(如 tokioasync-std)驱动 Future 的执行。

步骤 2:执行器工作流程

  1. 调用 process() 返回一个未执行的 Future
  2. .await 触发 Future 的轮询(poll)。
  3. Future 阻塞(如等待网络),执行器挂起任务并执行其他任务。
  4. 事件就绪时(如收到网络响应),执行器唤醒任务继续执行。

4. 关键特性

(1) 零成本抽象

  • Rust 的 async/await 在编译时转换为状态机,几乎没有运行时开销。

(2) 无堆内存分配(可选)

  • 简单 async 函数可在栈上分配状态,避免堆分配(如 no_std 环境)。

(3) 协作式调度

  • 任务通过 .await 主动让出控制权,需避免长时间阻塞:

    rust 复制代码
    // 错误:阻塞线程的操作!
    async fn bad_example() {
        std::thread::sleep(Duration::from_secs(2)); // 阻塞整个线程
    }
    
    // 正确:使用非阻塞等待
    async fn good_example() {
        tokio::time::sleep(Duration::from_secs(2)).await; // 让出控制权
    }

5. 组合异步任务

(1) 并发执行

rust 复制代码
use tokio::join;

async fn task_one() { /* ... */ }
async fn task_two() { /* ... */ }

async fn run_concurrent() {
    // 同时执行两个任务,等待全部完成
    let (result1, result2) = join!(task_one(), task_two());
}

(2) 选择最先完成的任务

rust 复制代码
use tokio::select;

async fn race_tasks() {
    select! {
        _ = task_one() => println!("Task 1 won"),
        _ = task_two() => println!("Task 2 won"),
    }
}

6. 错误处理

  • 与同步代码一致,使用 Result?
rust 复制代码
async fn fetch() -> Result<String, reqwest::Error> {
    let resp = reqwest::get("https://example.com").await?;
    resp.text().await
}

7. 底层原理:状态机转换

编译后,async 函数被转换为状态机:

rust 复制代码
// 伪代码:fetch_data() 编译后的状态机
enum FetchDataFuture {
    Start,
    Waiting(NetworkRequest),
    Done,
}

impl Future for FetchDataFuture {
    fn poll(&mut self) -> Poll<String> {
        match self {
            Start => {
                let request = start_network_request();
                *self = Waiting(request);
                Poll::Pending // 首次轮询未完成
            }
            Waiting(request) => {
                if request.is_ready() {
                    let data = request.get_data();
                    *self = Done;
                    Poll::Ready(data) // 完成时返回结果
                } else {
                    Poll::Pending // 继续等待
                }
            }
            Done => panic!("Future already completed"),
        }
    }
}

总结

特性 说明
async fn 定义异步函数,返回 Future
.await 等待 Future 完成(非阻塞)
执行器 (Executor) 驱动 Future 运行(如 tokio
零成本抽象 编译为高效状态机,无额外运行时开销
协作式调度 任务需主动让出 CPU,避免长阻塞操作

通过 async/await,Rust 在保证安全性和性能的同时,提供了直观的异步编程体验。

相关推荐
m0_480502642 天前
Rust 入门 生命周期-next2 (十九)
开发语言·后端·rust
寻月隐君2 天前
Rust Web 开发实战:使用 SQLx 连接 PostgreSQL 数据库
后端·rust·github
Moonbit3 天前
MoonBit Pearls Vol.05: 函数式里的依赖注入:Reader Monad
后端·rust·编程语言
Vallelonga3 天前
Rust 异步中的 Waker
经验分享·rust·异步·底层
m0_480502644 天前
Rust 入门 KV存储HashMap (十七)
java·开发语言·rust
Include everything4 天前
Rust学习笔记(三)|所有权机制 Ownership
笔记·学习·rust
码码哈哈爱分享4 天前
Tauri 框架介绍
css·rust·vue·html
寻月隐君5 天前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github
m0_480502645 天前
Rust 入门 泛型和特征-特征对象 (十四)
开发语言·后端·rust
RustFS5 天前
如何用 Rust 对 RustFS MCP Server 进行扩展?
rust