本文主要是介绍零成本异步 I/O (下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这是 Withoutboats 在 2019 年 3 月的 Rust Latam 上所做报告的一个整理。这个报告主要介绍他参与开发了一年半的语言特性,包括 Rust 异步 I/O 的发展历程,以及目前已经稳定的零成本抽象的
async
/await
语法的关键实现原理。Withoutboats 是就职于 Mozilla 的一名研究员,主要从事 Rust 语言开发。他开发的这个语言特性叫做
async
/await
,这可能是本年度我们在 Rust 语言上做的最重要的事。这解决了困扰我们很久的问题,即我们如何能在 Rust 中拥有零成本抽象的异步IO。注:因讲稿篇幅较长,所以分成上下两部分;因个人水平有限,翻译和整理难免有错误或疏漏之处,欢迎读者批评指正。
基于轮询的解决方案
// 基于轮询的 Future
trait Future {
type Output;
fn poll(&mut self, waker: &Waker)
-> Poll<Self::Output>;
}
enum Poll<T> {
Ready(T),
Pending,
}
这个非常出色的基于轮询的新方案——我们编写了这个模型,我归功于 Alex 和 Aaron Turon,是他们提出了这个想法——不是由 Future
来调度回调函数,而是由我们去轮询 Future
,所以还有另一个被称为执行器(executor)的组件,它负责实际运行 Future
;执行器的工作就是轮询 Future
,而 Future
可能返回“尚未准备就绪(Pending)”,也可能被解决就返回“已就绪(Ready)”。
该模型有很多优点。其中一个优点是,你可以非常容易地取消 Future
,因为取消 Future
只需要停止持有 Future
。而如果采用基于回调的方法,要通过调度来取消并使其停止就没这么容易了。
同时它还能够使我们在程序的不同部分之间建立真正清晰的抽象边界,大多数 Future
库都带有事件循环(event loop),这也是调度你的 Future
执行 I/O 的方法,但你实际上对此没有任何控制权。而在 Rust 中,各组件之间的边界非常整洁,执行器(executor)负责调度你的 Future
,反应器(reactor)处理所有的 I/O ,然后是你的实际代码。因此最终用户可以自行决定使用什么执行器,使用他们想使用的反应器,从而获得更强的控制力,这在系统编程语言中真的很重要。而此模型最重要的真正优势在于,它使我们能够以一种真正零成本的完美方式实现这种状态机式的 Future
。也就是当你编写的 Future
代码被编译成实际的本地(native)代码时,它就像一个状态机;在该状态机中,每次 I/O 的暂停点都有一个变体(variant),而每个变体都保存了恢复执行所需的状态。这表示为一个枚举(enum)结构,即一个包含变体判别式及所有可能状态的联合体(union)。
译者注:报告视频中的幻灯片比较模糊,我对其进行了重绘与翻译,下同。
上面的幻灯片尽可能直观地表示了这个状态机模型。可以看到,你执行了两个 I/O 事件,所以它有这几个状态。对于每个状态它都提供了所需的内存空间,足够你在 I/O 事件后恢复执行。
整个 Future
只需要一次堆内存分配,其大小就是你将这个状态机分配到堆中的大小,并且没有额外的开销。你不需要装箱、回调之类的东西,只有真正零成本的完美模型。这些概念对于很多人来说比较难于理解,所以这是我力求做到最好的幻灯片,直观地呈现这个过程中发生了什么:你创建一个 Future
,它被分配到某个内存中特定的位置,然后你可以在执行器(executor)中启动它。
执行器会轮询 Future
,直到最终 Future
需要执行某种 I/O 。
这篇关于零成本异步 I/O (下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!