【投稿】刀哥:Rust学习笔记 4

2024-06-23 00:08
文章标签 rust 学习 笔记 投稿 刀哥

本文主要是介绍【投稿】刀哥:Rust学习笔记 4,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

@[TOC](Rust 学习心得<4>:async/await 如何工作)

2019年底Rust正式支持 async/await语法,完成了Rust协程的最后一块拼图,从而异步代码可以用一种类似于Go的简洁方式来书写。然而对于程序员来讲,还是很有必要理解async/await的实现原理。

async

简单地说,async语法生成一个实现 Future 对象。如下async函数:

async fn foo() -> {...
}

async关键字,将函数的原型修改为返回一个Future trait object。然后将执行的结果包装在一个新的future中返回,大致相当于:

fn foo() -> impl Future<Output = ()> {async { ... }
}

更重要的是async 代码块会实现一个匿名的 Future trait object ,包裹一个 Generator。也就是一个实现了 Future 的 GeneratorGenerator实际上是一个状态机,配合.await当每次async 代码块中任何返回 Poll::Pending则即调用generator yeild,让出执行权,一旦恢复执行,generator resume 继续执行剩余流程。

以下是这个状态机Future的代码:

pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
whereT: Generator<ResumeTy, Yield = ()>,
{struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T);impl<T: Generator<ResumeTy, Yield = ()>> !Unpin for GenFuture<T> {}impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> {type Output = T::Return;fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };match gen.resume(ResumeTy(NonNull::from(cx).cast::<Context<'static>>())) {GeneratorState::Yielded(()) => Poll::Pending,  // 当代码无法继续执行,让出控制权,返回 Pending,等待唤醒GeneratorState::Complete(x) => Poll::Ready(x), // 执行完毕}}}GenFuture(gen)
}

可以看到这个特别的Future是通过Generator来运行的。每一次gen.resume()会顺序执行async block中代码直到遇到yieldasync block中的.await语句在无法立即完成时会调用yield交出控制权等待下一次resume。而当所有代码执行完,也就是状态机进入Completeasync block返回Poll::Ready,代表Future执行完毕。

await

每一个await本身就像一个执行器,在循环中查询Future的状态。如果返回Pending,则 yield,否则退出循环,结束当前Future

代码逻辑大致如下:

loop {match some_future.poll() {Pending => yield,Ready(x) => break}
}

为了更好地理解async/await的原理,我们来看一个简单例子:

async fn foo() {do_something_1();some_future.await;do_something_2();
}

使用async修饰的异步函数foo被改写为一个Generator状态机驱动的Future,其内部有一个some_future.await,中间穿插do_something_x()等其他操作。当执行foo().await时,首先完成do_something_1(),然后执行some_future.await,若some_future返回Pending,这个Pending被转换为yield,因此顶层foo()暂时也返回Pending,待下次唤醒后,foo()调用resume()继续轮询some_future,若some_future返回Ready,表示some_future.await完毕,则foo()开始执行do_something_2()

这里的关键点在于,因为状态机的控制,所以当foo()再次被唤醒时,不会重复执行do_something_1(),而是会从上次yield的的地方继续执行some_future.await,相当于完成了一次任务切换,这也是无栈协程的工作方式。

总结

async/await 通过一个状态机来控制代码的流程,配合Executor完成协程的切换。在此之后,书写异步代码不需要手动写Future及其poll方法,特别是异步逻辑的状态机也是由async自动生成,大大简化程序员的工作。虽然async/await出现的时间不长,目前纯粹使用async/await书写的代码还不是主流,但可以乐观地期待,今后更多的项目会使用这个新语法。

参考 Futures Explained in 200 Lines of Rust

这篇关于【投稿】刀哥:Rust学习笔记 4的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1085789

相关文章

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

rust 中的 EBNF简介举例

《rust中的EBNF简介举例》:本文主要介绍rust中的EBNF简介举例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 什么是 EBNF?2. 核心概念3. EBNF 语法符号详解4. 如何阅读 EBNF 规则5. 示例示例 1:简单的电子邮件地址

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

Rust中的注释使用解读

《Rust中的注释使用解读》本文介绍了Rust中的行注释、块注释和文档注释的使用方法,通过示例展示了如何在实际代码中应用这些注释,以提高代码的可读性和可维护性... 目录Rust 中的注释使用指南1. 行注释示例:行注释2. 块注释示例:块注释3. 文档注释示例:文档注释4. 综合示例总结Rust 中的注释

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

Rust中的Drop特性之解读自动化资源清理的魔法

《Rust中的Drop特性之解读自动化资源清理的魔法》Rust通过Drop特性实现了自动清理机制,确保资源在对象超出作用域时自动释放,避免了手动管理资源时可能出现的内存泄漏或双重释放问题,智能指针如B... 目录自动清理机制:Rust 的析构函数提前释放资源:std::mem::drop android的妙