前提
这篇文章主要描述了Rust中异步的原理,Rust异步也是在最近的版本中(1.39)中才稳定下来。希望可以通过这边文章在提高自己认知的情况下,也可以给读者带来一些解惑。(来自于本人被Rust异步毒打的一些经验之谈).
阅读这篇文章需要对操作系统,IO多路复用,以及一些数据结构有一定的概念。
Future
Future
字面的意思就是未来发生的事情,在程序中则代表了一系列暂时没有结果的运算子,Future
需要程序主动去poll
(轮询)才能获取到最终的结果,每一次轮询的结果可能是Ready
或者Pending
。
当Ready
的时候,证明当前Future
已完成,代码逻辑可以向下执行;当Pending
的时候,代表当前Future
并未执行完成,代码不能向下执行,看到这里就要问了,那什么时候才能向下执行呢,这里的关键在于Runtime
中的Executor
需要不停的去执行Future
的poll
操作,直至Future
返回Ready
可以向下执行为止。等等,熟悉Linux
的同学可能要说了,怎么感觉和Epoll
模型是非常的相似呢,没错,这确实非常相像(但是依然有些许不同,Future
可以避免空的轮询),看样子优秀的设计在哪里都可以看到类似的身影。为了实现Rust声称的高性能与零开销抽象,这里做了一些优化,下面一一讲述。
Future结构
pub enum Poll<T> {
Ready(T),
Pending,
}
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
Future
的定义非常简单,Output
代表了Future
返回的值的类型,而poll
方法是执行Future
的关键,poll
方法可以返回一个Poll
类型,Poll
类型是一个Enum
,包装了Ready
和Pending
两种状态。
Runtime
Runtime
由两部分组成,Executor
和Reactor
。
Executor
为执行器,没有任何阻塞的等待,循环执行一系列就绪的Future
,当Future
返回pending
的时候,会将Future
转移到Reactor
上等待进一步的唤醒。
Reactor
为反应器(唤醒器),轮询并唤醒挂在的事件,并执行对应的wake
方法,通常来说,wake
会将Future
的状态变更为就绪,同时将Future
放到Executor
的队列中等待执行。
执行流程
下面的序列图大概简单的描绘了Future
在Executor
和Reactor
之间来回转移的流程与状态变化。