乐趣区

关于c++:asyncawait-如何工作-Rust学习笔记

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。也就是一个实现了 FutureGeneratorGenerator 实际上是一个状态机,配合 .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 中代码直到遇到 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


作者:谢敬伟,江湖人称“刀哥”,20 年 IT 老兵,数据通信网络专家,电信网络架构师,目前任 Netwarps 开发总监。刀哥在操作系统、网络编程、高并发、高吞吐、高可用性等畛域有多年的实践经验,并对网络及编程等方面的新技术有浓重的趣味。


深圳星链网科科技有限公司(Netwarps),专一于互联网安全存储畛域技术的研发与利用,是先进的平安存储基础设施提供商,次要产品有去中心化文件系统(DFS)、企业联盟链平台(EAC)、区块链操作系统(BOS)。
微信公众号:Netwarps

退出移动版