vlambda博客
学习文章列表

Rust语言:异步编程,一个小故事带你入门

有这么一个有趣的故事:某家包子铺刚开业,生意十分火爆,很多人在店外排队等候购买。老板发现靠自己一人很难满足这么多人的消费,所以就又招了几个服务员,解决了排队时间过长的问题。随着互联网的兴起,老板购买了一个小程序系统,顾客可以提前下单,包子熟了自动发货到顾客的单位。

通过这个小故事我们发现,最初的经营方式是效率很低下,属于单生产者 - 单消费者模式。随后,更改为多生产者 - 单消费者模式,效率有所提升,可是顾客仍然需要排队等候购买。后来老板的新系统上线,才最终彻底解决了生产者和消费者等待的问题。

这就是我将要和大家分享的Rust异步编程,同步非阻塞,即代码书写是同步的,内部执行逻辑是非阻塞的。

什么是异步编程?

异步,是相对于同步而言的。同步,是指大家按照次序一个个来,上个顾客付完钱拿到了包子才能轮到下一位。而异步,就是上一个顾客付完钱拿到订单票号就让出来给下一位顾客了,等到包子熟了,自会按照订单票号呼叫到指定的人来拿包子即可。

Rust异步编程,使用Future来表示订单票号,使用Task来表示订单任务,使用Executor来表示包子铺厨子,使用Poll来表示橱子做包子用什么馅。

再描述的形象点,收银员处理了10位顾客的购买请求,打印了10张订单票号Future,将这10张订单票号组合成一个任务Task,交给了里面的师傅Executor来蒸包子,橱子看到Poll后大笑一声知道了,开始干活了。

Rust语言:异步编程,一个小故事带你入门

Future详解

Future是一种订单票号,该票号指定了最终得到的是什么物品,比如上文的包子。

官方核心代码定义如下:

Rust语言:异步编程,一个小故事带你入门

如果要创建一个Future,必须实现poll,告诉橱子用什么馅做食物,还必须指定食物是什么东西Output。

Task详解

Task是一系列Future的叠加。

Future可以是嵌套组合的,也可以是并列组合的。嵌套组合的意思是Future里包含了其他Future,一层层包含下去。并列组合的意思是几个互相独立的Future,合并在一起组合成一个新的Future。

无论如何,最终都是打包成一个可以具体操作的Task任务。

Executor详解

上面以包子、橱子等举例子,只为让大家能更容易的接受Rust复杂的异步实现当中的几大角色。

而真正在一线工厂干活的,是这个底层的Executor,当然也是非常重要的一个角色,内部实现原理还是有点复杂的。

不知道各位是否看过知乎的“深入浅出Rust异步编程”,有些概念比如Waker、Park、Context、Schedulor等等,要搞明白这些概念还是需要花很多时间研读标准库和tokio库的源码。

这里简单的讲下他们各自的作用。

Waker的作用是提供Task切换时上下文现场的保存和还原。

Park的作用是管理当前线程的运行状态。

Context是Waker的封装,提供操作Waker的入口,因为Waker涉及到重新创建的问题。

写在最后

我感觉这个选题有点过大了,很多地方很难通过几百字就能阐述清楚,而且我本人对tokio的runtime源码也没有完成的研究明白,贸然去写内部的原理,可能会“以己昏昏使人昭昭”。

所以,只能从表象上给大家讲一讲大概的原理,浅述一下什么是Rust的异步编程,深感惭愧。

我也希望能够彻底的搞明白tokio的runtime调度原理,在以后有机会更详细的给大家理一理这一块。