接下來幾篇文章要來探討在Rust中的非同步程式設計,主要內容參考Rust的async book,並且與C#中的Task
做比較。
rust採取的非同步模型來源自Functional Programming的Future Monad,與C#中的Task
概念極為相似,事實上很多語言都是基於這個模型來進行非同步程式設計(FP沒有想像中的遙遠)。我會先從C#的Task
開始介紹,並深入rust future
的底層實做。
現在先讓我們回到C# 4.0還不支援async/await的時期,Task的使用方式大概是這樣:
var task = Task.Factory.StartNew(DoSomething);
task.Wait();
Console.WriteLine("task is completed,result is {0}", task.Result);
在上面的範例中,使用執行緒封鎖的方式task.Wait()
來等待任務完成,期間會佔用一個執行緒不能做其他事情,其實非常的沒效率,我們可以稍微改一下:
var task = Task.Factory.StartNew(DoSomething);
while(!task.IsCompleted)
{
DoAnotherthing1();
DoAnotherthing2();
DoAnotherthing3();
}
Console.WriteLine("task is completed,result is {0}", task.Result);
在這邊使用一個while迴圈不斷的詢問task是否完成,尚未完成的話執行緒可以先做其他事情,等到task完成後再印出結果(先忽略迴圈的內容會一直被重做)。
C# 5.0 就加入了async/await
關鍵字,讓我們再改一下程式碼:
var task = DoSomethingAsync();
DoAnotherthing1();
DoAnotherthing2();
DoAnotherthing3();
Console.WriteLine("task is completed,result is {0}", await task);
上面的的這段程式碼中,當呼叫DoSomethingAsync()後就會開始一個非同步任務,同時可以DoAnotherthing
,最後等待task的結果,過程中還不會有重複做DoAnotherthing的狀況,一切都歸功於async/await的語法糖。那麼這背後到底隱藏什麼魔法呢?其實就是透過C#的runtime在背景中有一個任務排程器在不斷的替你詢問IsCompleted
(好啦沒那麼白痴,通常都是靠類似事件的機制通知排程器任務的進度)。
上面的故事告訴我們,非同步任務的最外面一定是由一個神奇的同步程式處理(就是while的角色),現在我們來回憶一下最一開始建構Axum章節,把tokio的巨集拆開後有一個同步的block_on
方法,tokio runtime扮演的角色就是在背後對async方法進行任務調度,在每一個Task完成的時候將非同步程式的進度往前推進。
明天要來介紹rust中的future trait,實做了future trait的東西就相當於C#中的Task。