Rust异步编程之async与.await

异步编程主要用于高并发 I/O 密集型任务;Rust 的异步模型基于 Future + Polling,在单线程上即可完成数千个并发任务。async/.await是Rust内置语法,用于让异步函数编写得像同步代码。async将代码块转化成实现了Future trait 的状态机。

async

async 是用于创建 Future 的语法糖,用来标记一个函数为异步函数:

  • 异步函数(async fn):调用后不会立即执行,而是返回一个 Future
    • 返回值会被自动包装为 Future;
    • 异步函数内部可以直接使用 .await
    • 异步函数的参数支持所有普通函数的参数类型(包括引用、所有权类型等),但要注意生命周期;
  • 异步块(async { ... }):async 块可以捕获外部变量(类似闭包)
    • async { ... }:默认像闭包捕获,按借用捕获外部变量;
    • async move { ... }:把需要的变量按值move进 Future,Future 对它们具有所有权。
  • 异步闭包
// 异步函数
async fn fetch_data() -> u32 {
    42
}

// 异步块
let fut = async {
    println!("Working...");
    100
};

// 异步闭包
let async_func = async |x: i32| x + 1;

.await

.await 是用于等待 Future 执行完成的操作符;只能在async函数或async块内部使用。

对 Future 调用 .await,会暂停当前 Future 的执行(并将线程让给其他可执行的 Future),直到被等待的 Future 完成,然后返回其 Output 结果。

Rust中代码编译扫描时,把每一个可能“挂起”的 .await 看作一个边界,把整个函数拆成几段;同时定义状态机,把后续要继续执行所需的局部变量 保存下来。

// 原始示例函数
async fn foo() -> i32 {
    let x = 1;
    let y = async { 2 }.await;

    let z = x + y;
    let w = async { z * 10 }.await;

    w
}

//////////////////////////////////////////
// 翻译的伪代码
enum FooState {
    // 初始状态
    Start,
    // 正在等待第一个 .await: async { 2 }
    AwaitY {
        // 需要保存仍然会用到的局部变量
        x: i32,
        // 正在等待的子 future
        fut_y: FutY,
    },
    // 正在等待第二个 .await: async { z * 10 }
    AwaitW {
        // 计算 w 之前需要的变量
        z: i32,
        fut_w: FutW,
    },
    // 已经完成(再 poll 就是错误)
    Done,
}

// 整个 async 块对应的 Future 类型
struct FooFuture {
    state: FooState,
}

// 子 future 的具体类型编译器会生成匿名类型,这里用伪名代替
type FutY = impl Future<Output = i32>;
type FutW = impl Future<Output = i32>;

// 核心状态机
impl Future for FooFuture {
    type Output = i32;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // 安全地拿到可变引用
        let this = unsafe { self.get_unchecked_mut() };

        loop {
            match &mut this.state {
                FooState::Start => {
                    // 1. 执行:let x = 1;
                    let x = 1;

                    // 2. 创建第一个子 Future:async { 2 }
                    let fut_y: FutY = async { 2 };

                    // 3. 进入 AwaitY 状态,保存需要的变量
                    this.state = FooState::AwaitY { x, fut_y };

                    // loop 继续,会立刻进入下一轮 match(相当于“尾递归优化”)
                }

                FooState::AwaitY { x, fut_y } => {
                    // 尝试推进子 future
                    match Pin::new(fut_y).poll(cx) {
                        Poll::Pending => {
                            // 子 future 还没好:当前 async 函数整体也只能 Pending
                            return Poll::Pending;
                        }
                        Poll::Ready(y) => {
                            // some_future().await 完成,得到了 y

                            // 3. 计算 z = x + y
                            let z = *x + y;

                            // 4. 创建第二个子 Future:async { z * 10 }
                            let fut_w: FutW = async { z * 10 };

                            // 5. 切换到 AwaitW 状态
                            this.state = FooState::AwaitW { z, fut_w };

                            // 再次进入 loop,转到下一个 match 分支
                        }
                    }
                }

                FooState::AwaitW { z, fut_w } => {
                    // 推进第二个子 future
                    match Pin::new(fut_w).poll(cx) {
                        Poll::Pending => {
                            return Poll::Pending;
                        }
                        Poll::Ready(w) => {
                            // 第二个 await 完成,最终结果就是 w

                            this.state = FooState::Done;
                            return Poll::Ready(w);
                        }
                    }
                }

                FooState::Done => {
                    panic!("poll called after completion");
                }
            }
        }
    }
}

Future

Future (std::future::Future)是 Rust 异步编程的基础,async 语法的本质是生成 Future,.await 则是触发 Future 执行并等待结果。

trait Future {
    // Future 最终产生的结果类型
    type Output;

    // 驱动 Future 执行:返回 Poll<Output>
    // waker:用于唤醒暂停的 Future
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

// Poll 枚举:表示 Future 的执行状态
enum Poll<T> {
    // Future 仍在执行中(需要等待某个事件,如 IO 完成)
    Pending,
    // Future 已完成,返回结果 T
    Ready(T),
}

Future 是惰性的—— 定义后不会自动执行,必须通过 poll 方法 “驱动” 它运行。只有当调用者(通常是异步执行器)调用 poll 时,Future 才会推进执行,直到遇到 “需要等待的事件”(如网络请求、文件 IO),此时返回 Pending 并暂停,等待被 waker 唤醒后再次执行。

执行器

要运行一个异步函数(async fn),必须使用一个 执行器(Runtime / Executor) 来驱动它

场景解决方法代码示例
应用程序入口使用宏#[tokio::main]
async fn main() { ...}
普通函数中手动创建 RuntimeRuntime::new().unwrap().block_on(future)
轻量级/测试使用简单执行器futures::executor::block_on(future)

async/.await 本身是串行的,若要并发执行多个 Future,需使用并发宏:

  • tokio::join!/async_std::future::join!:等待所有 Future 完成(类似 Promise.all);
  • tokio::select!/async_std::future::select!:等待第一个完成的 Future(类似 Promise.race);
  • tokio::try_join!:并发执行,且所有 Future 均返回 Result,一旦有一个失败则立即返回错误。

运行时(Runtime)

运行时是驱动异步任务(Future)执行的 “引擎”,负责线程池管理、任务调度、IO 事件复用(如 epoll/kqueue)等底层工作:

  1. Executor(执行器)
    • 负责不断调用 Future::poll() 推进任务执行。
    • 支持任务调度、任务队列、线程池等。
  2. Reactor(反应器)
    • 基于操作系统的 IO 多路复用机制(epoll/Linux、kqueue/macOS、IOCP/Windows)。
    • 当 socket、文件或计时器可用时,唤醒对应任务。
  3. Concurrency Primitives(并发原语)
    • 提供异步锁(如 Mutex)、通道(Channel)、计时器(Timer)等工具,支持多任务安全通信。

常见运行时库

特性Tokioasync-stdsmol
性能最高(多线程调度)中等轻量,适中
使用体验偏工程化、配置丰富最接近标准库极简、底层
生态支持最强(hyper, tonic, reqwest)中等少量核心库
内部实现work-stealing + reactorsmol 内核模块化组件
适合场景高并发服务端快速开发、教学小型嵌入式、自定义运行时
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值