当前位置: 首页 > news >正文

【Rust】16-async/await、Future 与执行器模型

async/await、Future 与执行器模型

研究目标

  • 理解 Rust async 不是创建线程,而是生成状态机。
  • 掌握FutureWaker、executor 的基本关系。
  • 知道 async 代码中所有权、生命周期和 Send 约束为何常见。

async 的核心模型

Rust 的async fn会返回一个实现了Future的值。调用 async 函数本身并不会立即执行完整逻辑,它只是构造一个 future。

asyncfnfetch()->String{String::from("data")}fnmain(){letfuture=fetch();// future 还没有被执行到完成}

future 必须被执行器轮询,才会向前推进。常见执行器包括 Tokio、async-std、smol,也可以在测试或嵌入式环境中使用专门执行器。

Future Trait

简化后的Futuretrait 可以理解为:

usestd::pin::Pin;usestd::task::{Context,Poll};traitFuture{typeOutput;fnpoll(self:Pin<&mutSelf>,cx:&mutContext<'_>)->Poll<Self::Output>;}

poll返回两种状态:

  • Poll::Ready(value):计算完成。
  • Poll::Pending:暂时不能完成,稍后再来轮询。

执行器反复轮询 future。当 future 因等待 IO、定时器或其他事件无法继续时,返回Pending,并通过Waker告诉执行器将来什么时候再唤醒它。

await 做了什么

.await会在当前 future 内等待另一个 future 完成:

asyncfnhandle()->usize{lettext=read_text().await;text.len()}asyncfnread_text()->String{String::from("hello")}

编译器会把handle转换成状态机。read_text().await是一个可能暂停的位置。暂停时,当前函数的局部变量需要被保存到 future 对象内部,等唤醒后继续执行。

async 状态机

可以把 async 函数想象成枚举状态机:

enumHandleFuture{Start,WaitingReadText,Done,}

真实生成代码更复杂,但关键直觉是:跨越.await的局部变量会成为 future 状态的一部分。这也是为什么 async 代码经常遇到所有权和生命周期问题。

Pin 的作用

某些 future 内部可能自引用:状态机里一个字段引用另一个字段。这样的对象一旦被移动,内部引用就可能失效。Pin用于表达“这个值不能再被随意移动”的约束。

普通用户很少需要手写Pin,但理解它有助于解释为什么Future::poll的接收者是Pin<&mut Self>。async/await 让这些复杂性大多被编译器和运行时封装起来。

Waker 与唤醒

当 future 返回Pending时,它必须确保在将来可以继续时调用 waker:

executor poll future future waits for IO future stores waker future returns Pending IO ready waker wakes task executor polls future again

如果 future 返回Pending但没有正确安排唤醒,任务可能永远卡住。执行器和 IO reactor 的配合负责处理这些细节。

执行器模型

执行器负责调度任务。一个常见模型是:

  1. 任务队列保存可运行 future。
  2. 执行器 poll 某个任务。
  3. 如果 Ready,任务完成。
  4. 如果 Pending,任务让出执行权。
  5. 外部事件通过 waker 把任务放回队列。

这和操作系统线程不同。async 任务通常是协作式调度:只有在.await等挂起点才会让出执行权。一个没有 await 的长 CPU 循环会阻塞同一执行器线程上的其他任务。

Tokio 示例

#[tokio::main]asyncfnmain(){lettask=tokio::spawn(async{"hello"});letresult=task.await.unwrap();println!("{result}");}

tokio::spawn通常要求 future 是Send + 'static,因为任务可能在线程池中被移动到其他线程执行,并且执行器不能依赖当前栈帧里的短生命周期引用。

Send 约束常见来源

下面的模式容易出问题:

usestd::rc::Rc;asyncfnwork(){letvalue=Rc::new(1);some_async().await;println!("{value}");}asyncfnsome_async(){}

Rc<T>不是Send。如果value跨越.await存活,那么整个 future 可能不是Send。在多线程执行器中,这类 future 不能被spawn

修复方式取决于需求:

  • 使用Arc<T>替代Rc<T>
  • 让非 Send 值不跨越.await
  • 使用单线程执行器或spawn_local

async 与借用

跨 await 持有借用也需要谨慎:

asyncfnprint_later(text:&str){wait().await;println!("{text}");}asyncfnwait(){}

这个 future 的生命周期依赖text。如果要把它放入要求'static的任务中,就不能借用当前栈上的字符串。常见做法是传入拥有所有权的StringArc<str>

阻塞操作

async 代码中不能随意执行阻塞操作:

std::thread::sleep(std::time::Duration::from_secs(1));

这会阻塞执行器线程。应使用运行时提供的异步版本:

tokio::time::sleep(std::time::Duration::from_secs(1)).await;

文件 IO、数据库驱动、HTTP 客户端也应选择 async 兼容版本,或者放到专门的 blocking 线程池。

常见误解

  • async fn调用后不会自动跑完,必须被 await 或 spawn。
  • async 不等于并行;它主要解决等待期间让出执行权。
  • .await是可能暂停点,跨越它的变量会影响 future 类型。
  • Send + 'static错误通常来自任务调度模型,不是编译器无理限制。

继续研究

  • Rust Async Book:Future、task wakeups、executor。
  • Rust Reference:async functions、async blocks、await expressions。
  • Tokio 文档:runtime、task、spawn、spawn_blocking。
  • futures crate:FutureExt、Stream、select、join。

后记

2026年6月11日15点21分于上海。

http://www.zskr.cn/news/1509401.html

相关文章:

  • 搬家寄快递这样打包,省钱又省心 - 快递物流资讯
  • Python实现的朴素贝叶斯邮件分类器,含训练样本与可运行代码
  • MATLAB刀具路径B样条拟合与拐点平滑衔接工具包
  • 2026拼多多代运营公司推荐:百亿补贴+拼便宜组合拳,销量利润双增长 - 百推信源
  • 2026年通辽装修公司深度对比:全屋定制硬核差距惊人拆解 - 国麟测评
  • 2026年重型货架厂家怎么选?从台州、成都到中山,这些正规厂商值得关注! - 优质品牌商家
  • Windows下可直接运行的模板旋转匹配工具:自动输出XY坐标和旋转角度
  • 【Rust】18-宏系统:声明宏、过程宏与代码生成
  • 深入MAX30102算法核心:手把手解读心率血氧计算函数,告别‘黑盒’调用
  • 从EMV到物联网:TLV编码的前世今生与实战避坑指南
  • 从Betaflight到Ardupilot:为什么你的AT32飞控板还跑不了?聊聊ChibiOS移植的那些坑
  • 从V1到V3:MobileNet家族进化史,看谷歌如何用‘倒残差’和SE模块把模型越做越小
  • 3个步骤,让计算机学会“审美“:AI图像质量评估实战指南
  • Python-docx进阶玩法:手动控制迭代,精准处理Word中的图文表混合内容
  • 百度网盘解析工具终极指南:快速获取真实下载地址,告别龟速下载
  • 从时序报告反推约束:手把手教你解读set_clock_transition对setup/hold time的影响
  • 基于逆向工程的百度网盘直链解析技术深度解析
  • MATLAB小波分析工具包:一维信号四层Mallat分解与精确重构(含db10示例)
  • STM32H743实战:从DMA2D访问SRAM1,搞懂D1/D2/D3域互联的AHB总线矩阵
  • 终极百度网盘提取码查询工具:10秒解锁任何分享资源
  • Python 高手编程系列三千四百四十一:有用的工具
  • 从5000个Case到50个:资深验证工程师教你用正交矩阵法高效分解测试点
  • 鼎阳示波器选件机制解析:从软件密钥生成到硬件功能验证,我们聊点干货
  • 纯HTML图像热点区域实现:支持rect/circle/poly三种形状,兼容Chrome/Firefox/Safari/Edge/IE11
  • 网盘直链解析终极指南:一键解锁高速下载的完整解决方案
  • 常州离婚财产分割纠纷难解决?2026年这5位离婚律师推荐 - 本地品牌推荐
  • Windows虚拟声卡Scream终极教程:让音频在局域网内自由飞翔的完整指南
  • 广东寄大件,怎么寄最省钱?这份技巧请收好 - 快递物流资讯
  • ARMv8异常处理避坑指南:调试那些年遇到的Data Abort和SError(含GIC配置)
  • 3分钟掌握百度网盘提取码智能获取:告别手动搜索的5个高效技巧