做者:謝敬偉,江湖人稱「刀哥」,20年IT老兵,數據通訊網絡專家,電信網絡架構師,目前任Netwarps開發總監。刀哥在操做系統、網絡編程、高併發、高吞吐、高可用性等領域有多年的實踐經驗,並對網絡及編程等方面的新技術有濃厚的興趣。html
2019年末Rust
正式支持 async/await語法,完成了Rust
協程的最後一塊拼圖,從而異步代碼能夠用一種相似於Go
的簡潔方式來書寫。然而對於程序員來說,仍是頗有必要理解async/await
的實現原理。git
簡單地說,async
語法生成一個實現 Future
對象。以下async
函數:程序員
async fn foo() -> { ... }
async
關鍵字,將函數的原型修改成返回一個Future trait object
。而後將執行的結果包裝在一個新的future
中返回,大體至關於:github
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
自己就像一個執行器,在循環中查詢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
書寫的代碼還不是主流,但能夠樂觀地期待,從此更多的項目會使用這個新語法。
深圳星鏈網科科技有限公司(Netwarps),專一於互聯網安全存儲領域技術的研發與應用,是先進的安全存儲基礎設施提供商,主要產品有去中心化文件系統(DFS)、企業聯盟鏈平臺(EAC)、區塊鏈操做系統(BOS)。微信公衆號:Netwarps