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
的
Generator
。
Generator
其实是一个状态机,配合
.await
当每次
async
代码块中任何返回
Poll::Pending
则即调用
generator yeild
,让出执行权,一旦恢复执行,
generator resume
继续执行剩余流程。
Future
的代码:
pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
where
T: 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
中代码直到遇到
yield
。
async block
中的
.await
语句在没法当即完成时会调用
yield
交出控制权等待下一次
resume
。而当全部代码执行完,也就是状态机进入
Complete
,
async 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语言中文社区(rust-china)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。程序员