Rust 異步狀態機
這篇文章通過一個簡單的例子,介紹一下 Rust 編譯器爲 async/await 生成的狀態機。
Future 狀態機的簡單概述如下圖:
在下面的代碼示例中,我們創建了一個頂層的 future:fut_top,它依賴於 FakeFuture 對象:fake。我們只是簡單地使用 poll 方法來驅動 fut_top 直到完成。另外,需要在 Cargo.toml 文件中引入依賴庫:
[dependencies]
futures = "0.3.1"
use futures::{
task::{waker_ref, ArcWake}, // to create `context` from type implements ArcWake trait
FutureExt, // to use `Future::boxed` method
};
use std::{
future::Future,
sync::Arc,
task::{Context, Poll},
};
async fn fut_top() {
println!("poll top future");
let fake = FakeFuture;
fake.await;
}
struct FakeFuture;
impl Future for FakeFuture {
type Output = ();
fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
static mut SWITCH: bool = false;
println!("poll fake future");
unsafe {
if !SWITCH {
SWITCH = true;
return Poll::Pending;
}
}
Poll::Ready(())
}
}
fn run(f: impl Future<Output = ()> + Send + 'static) {
struct DummyTask;
impl ArcWake for DummyTask {
fn wake_by_ref(_arc_self: &Arc<Self>) {
todo!()
}
}
let task = Arc::new(DummyTask);
let waker = waker_ref(&task);
let context = &mut Context::from_waker(&*waker);
let mut f = f.boxed();
while f.as_mut().poll(context).is_pending() {
println!("pending...");
}
}
fn main() {
let f = fut_top();
println!("start to drive future!");
run(f);
println!("future completed!");
}
fut_top() 函數簽名前加入 async,編譯器會自動實現的 future trait,生成狀態機。當且僅當 fut_top 所依賴的 future 完成時,狀態纔是 "Poll::Ready"。從執行者的角度看 "fut_top" 是一個任務,任務是已經提交給執行者執行的頂層 future。
"fut_top" 生成的狀態機將依賴於 "fake" 的未來狀態,當我們 poll “fut_top” 時,它會 poll “fake” 的 future。
在 unsafe 語法塊中,我們只演示返回的狀態是如何影響狀態機的,實際上,它是由 “context” 管理的,例如 x.waker().wake_by_ref()。
爲了完成 future,我們只需要調用 “poll”。但是,爲了滿足 "poll" 接口,我們需要創建一個 "context" 對象。在這裏我們需要模擬一個 context 對象,所以需要創建一個 Dummy Task 類型,並從它創建一個 context 對象。
運行 cargo run 輸出:
start to drive future!
poll top future
poll fake future
pending...
poll fake future
future completed!
本文翻譯自:
https://medium.com/@gftea/async-state-machine-in-rust-e8d46af52532
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aIpcBw6Anz4V5fLc1sOhlA