@[toc](rust 學習心得<4>:async/await 如何工作)
2023年底rust
正式支援 async/await語法,完成了rust
協程的最後一塊拼圖,從而非同步**可以用一種類似於go
的簡潔方式來書寫。然而對於程式設計師來講,還是很有必要理解async/await
的實現原理。
簡單地說,async
語法生成乙個實現future
物件。如下async
函式:
async fn foo() ->
async
關鍵字,將函式的原型修改為返回乙個future trait object
。然後將執行的結果包裝在乙個新的future
中返回,大致相當於:
fn foo() -> impl future
}
更重要的是async
**塊會實現乙個匿名的future trait object
,包裹乙個generator
。也就是乙個實現了future
的generator
。generator
實際上是乙個狀態機,配合.await
當每次async
**塊中任何返回poll::pending
則即呼叫generator yeild
,讓出執行權,一旦恢復執行,generator resume
繼續執行剩餘流程。
以下是這個狀態機future
的**:
pub const fn from_generator(gen: t) -> impl futurewhere
t: generator,
impl> future for genfuture;
match gen.resume(resumety(nonnull::from(cx).cast::>())) }}
genfuture(gen)
}
可以看到這個特別的future
是通過generator
來執行的。每一次gen.resume()
會順序執行async block
中**直到遇到yield
。async block
中的.await
語句在無法立即完成時會呼叫yield
交出控制權等待下一次resume
。而當所有**執行完,也就是狀態機進入complete
,async block
返回poll::ready
,代表future
執行完畢。
每乙個await
本身就像乙個執行器,在迴圈中查詢future
的狀態。如果返回pending
,則yield
,否則退出迴圈,結束當前future
。
**邏輯大致如下:
loop
}
為了更好地理解async/await
的原理,我們來看乙個簡單例子:
async fn foo()
使用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學習筆記
toc rust學習筆記 初步閱讀rust的 以學習rust語言 match 採用表示式的形式,然後根據它的值來分支。分支的每個 臂 都是 val expression 的形式。當值匹配時,這個臂的表示式將被執行實現。之所以稱之為 match 是因為 模式匹配 的術語,而這種正是 match 實現的...
Rust學習筆記1
use rand rng use std cmp ordering use std io fn main println 輸入數字 input match input.cmp guess ordering greater println 高了 ordering less println 低了 對比c...
rust筆記4 slice型別
先給出個例子 fn main n n n n slice0,slice1,slice2,slice3,slice4 輸出結果 hello hello world world hello world 切片的區間是左閉右開,而且切片是引用型別的,這相當於位址的乙個範圍,所以我們使用 s的方式來表示。字串...