日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

关于async和await的探讨

發布時間:2023/12/4 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于async和await的探讨 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

緣起

最近在看《深入解析C#(第4版)》這本書,看到了第五章,這一章節是關于異步。之前對異步這個概念只能算是一知半解,了解了它的概念和用法,但是對它的實際場景和為了解決什么問題而誕生的是不太清楚的。于是乎,就和小伙伴之間有了一場討論。

概念

一般來說對方法的調用都是同步執行的。例如在線程執行體內,即線程的調用函數中,方法的調用就是同步執行的。如果方法需要很長的時間來完成,比方說從Internet加載數據的方法,調用者線程將被阻塞直到方法調用完成。這時候為了避免調用者線程被阻塞,這時候就需要用到異步編程了。異步編程可以解決線程因為等待獨占式任務而導致的阻塞問題。

探索

探索過程中,參考了《微軟官方文檔》,《I/O Threads Explained》。

例子說明

官方以一個做早餐的例子來解釋了什么叫同步,并行和異步。

假設做一個早餐需要完成7個步驟:

  • 倒一杯咖啡。

  • 加熱平底鍋,然后煎兩個雞蛋。

  • 煎三片培根。

  • 烤兩片面包。

  • 在烤面包上加黃油和果醬。

  • 倒一杯橙汁。

  • 同步執行

    同步執行,是指只有完成上一個任務,才會開始下一個任務;同時將阻塞當前線程執行其他操作,直至任務全部完成

    代碼例子如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89// 鑒于我用的是vs2022,可能控制臺程序的代碼在舊版本的vs上無法直接運行,需要補充對應的main函數 MakeBreakfast();static void MakeBreakfast() {var cup = PourCoffee();Console.WriteLine("coffee is ready");var eggs = FryEggs(2);Console.WriteLine("eggs are ready");var bacon = FryBacon(3);Console.WriteLine("bacon is ready");var toast = ToastBread(2);ApplyButter(toast);ApplyJam(toast);Console.WriteLine("toast is ready");var oj = PourOJ();Console.WriteLine("oj is ready");Console.WriteLine("Breakfast is ready!"); }static Juice PourOJ() {Console.WriteLine("Pouring orange juice");return new Juice(); }static void ApplyJam(Toast toast) => Console.WriteLine("Putting jam on the toast");static void ApplyButter(Toast toast) =>Console.WriteLine("Putting butter on the toast");static Toast ToastBread(int slices) {for (int slice = 0; slice < slices; slice++){Console.WriteLine("Putting a slice of bread in the toaster");}Console.WriteLine("Start toasting...");Task.Delay(3000).Wait();Console.WriteLine("Remove toast from toaster");return new Toast(); }static Bacon FryBacon(int slices) {Console.WriteLine($"putting {slices} slices of bacon in the pan");Console.WriteLine("cooking first side of bacon...");Task.Delay(3000).Wait();for (int slice = 0; slice < slices; slice++){Console.WriteLine("flipping a slice of bacon");}Console.WriteLine("cooking the second side of bacon...");Task.Delay(3000).Wait();Console.WriteLine("Put bacon on plate");return new Bacon(); }static Egg FryEggs(int howMany) {Console.WriteLine("Warming the egg pan...");Task.Delay(3000).Wait();Console.WriteLine($"cracking {howMany} eggs");Console.WriteLine("cooking the eggs ...");Task.Delay(3000).Wait();Console.WriteLine("Put eggs on plate");return new Egg(); }static Coffee PourCoffee() {Console.WriteLine("Pouring coffee");return new Coffee(); }public class Juice { }public class Bacon { }public class Egg { }public class Coffee { }public class Toast { }

    同步執行的總耗時是每個任務耗時的總和。此外,因為是同步執行的原因,在開始制作一份早餐的時候,如果此時又有一份制作早餐的請求過來,是不會開始制作的。如果是客戶端程序,使用同步執行耗時時間長的操作,會導致UI線程被阻塞,導致UI線程無法響應用戶操作,直至操作完成后,UI線程才相應用戶的操作。

    異步執行

    異步執行,是指在遇到await的時候,才需要等待異步操作完成,然后往下執行;但是不會阻塞當前線程執行其他操作。

    代碼如下

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60await MakeBreakfastAsync();static async Task MakeBreakfastAsync() {var cup = PourCoffee();Console.WriteLine("coffee is ready");var eggs = await FryEggsAsync(2);Console.WriteLine("eggs are ready");var bacon = await FryBaconAsync(3);Console.WriteLine("bacon is ready");var toast = await ToastBreadAsync(2);ApplyButter(toast);ApplyJam(toast);Console.WriteLine("toast is ready");var oj = PourOJ();Console.WriteLine("oj is ready");Console.WriteLine("Breakfast is ready!"); }static async Task<Toast> ToastBreadAsync(int slices) {for (int slice = 0; slice < slices; slice++){Console.WriteLine("Putting a slice of bread in the toaster");}Console.WriteLine("Start toasting...");Task.Delay(3000).Wait();Console.WriteLine("Remove toast from toaster");return await Task.FromResult(new Toast()); }static Task<Bacon> FryBaconAsync(int slices) {Console.WriteLine($"putting {slices} slices of bacon in the pan");Console.WriteLine("cooking first side of bacon...");Task.Delay(3000).Wait();for (int slice = 0; slice < slices; slice++){Console.WriteLine("flipping a slice of bacon");}Console.WriteLine("cooking the second side of bacon...");Task.Delay(3000).Wait();Console.WriteLine("Put bacon on plate");return Task.FromResult(new Bacon()); }static Task<Egg> FryEggsAsync(int howMany) {Console.WriteLine("Warming the egg pan...");Task.Delay(3000).Wait();Console.WriteLine($"cracking {howMany} eggs");Console.WriteLine("cooking the eggs ...");Task.Delay(3000).Wait();Console.WriteLine("Put eggs on plate");return Task.FromResult(new Egg()); }

    上面代碼只是為了避免堵塞當前的線程,并沒有真正用上異步執行的某些關鍵功能,所以在耗時上是相差不遠的;但是這時候如果在接受了一份制作早餐的請求,還未完成的時候,又有一份制作早餐的請求過來,是可能會開始制作另一份早餐的。

    改善后的異步執行

    代碼如下

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25await MakeBreakfastBetterAsync();static async Task MakeBreakfastBetterAsync() {Coffee cup = PourCoffee();Console.WriteLine("Coffee is ready");Task<Egg> eggsTask = FryEggsAsync(2);Task<Bacon> baconTask = FryBaconAsync(3);Task<Toast> toastTask = ToastBreadAsync(2);Toast toast = await toastTask;ApplyButter(toast);ApplyJam(toast);Console.WriteLine("Toast is ready");Juice oj = PourOJ();Console.WriteLine("Oj is ready");Egg eggs = await eggsTask;Console.WriteLine("Eggs are ready");Bacon bacon = await baconTask;Console.WriteLine("Bacon is ready");Console.WriteLine("Breakfast is ready!"); }

    異步方法的邏輯沒有改變,只是調整了一下代碼的執行順序,一開始就調用了三個異步方法,只是在await語句后置了,而不是上面那段代碼一樣,執行了就在那里等待任務完成,而是會去進行其他的后續操作,直至后續操作需要用到前面任務執行結果的時候,才去獲取對應的執行結果,如果沒有執行完成就等待執行完成才繼續后續的操作。

    異步執行并不總是需要另一個線程來執行新任務。并行編程是異步執行的一個子集。

    并行編程

    并行編程,調用多個線程,同時去執行任務

    例如:需要制作五份早餐,同步和異步的方法都是需要循環調用相應的MakeBreakfast方法和MakeBreakfastBetterAsync方法五次才能制作完成。而并行編程,也就是多線程,可以一次性創建五個線程,分別制作一份早餐,從而大大縮短了所需要的時間。

    代碼如下

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34DateTime beforeDT = DateTime.Now; for (int i = 0; i < 5; i++) {MakeBreakfast(); } DateTime afterDT = DateTime.Now; TimeSpan ts = afterDT.Subtract(beforeDT); Console.WriteLine($"同步執行程序耗時: {ts.TotalMilliseconds}ms");beforeDT = DateTime.Now; for (int i = 0; i < 5; i++) {await MakeBreakfastBetterAsync(); } afterDT = DateTime.Now; ts = afterDT.Subtract(beforeDT); Console.WriteLine($"異步執行程序耗時: {ts.TotalMilliseconds}ms");beforeDT = DateTime.Now; await MakeBreakfastBetterMultiTask(); afterDT = DateTime.Now; ts = afterDT.Subtract(beforeDT); Console.WriteLine($"并行編程程序耗時: {ts.TotalMilliseconds}ms");static async Task MakeBreakfastBetterMultiTask() {Task[] tasks = new Task[5];for (int i = 0; i < 5; i++){tasks[i] = new Task((parameter) => MakeBreakfastBetterAsync().Wait(), "aaa");tasks[i].Start();}Task.WaitAll(tasks); }

    運行耗時結果如下

    相比之下,顯然能看出來之間的運行耗時差別還是有點大的。

    一個通俗的例子

    程序就像一個餐館,線程就像餐館里面已有的廚師,CPU就是調度廚師的廚師長,假設餐館開業了,廚師長只帶了5個廚師,餐館接到的訂單有8份,同步執行就是5個廚師分別處理5個訂單后,這期間,他們會專心的去完成訂單的菜,而無視其他的事情,直到完成訂單,廚師長才會分配新的訂單給他們;異步執行則是5個廚師在處理5個訂單的期間,如果廚師長發現他們有人處于空閑狀態,就會安排他們去執行剩下3個訂單,如果收到等待中的訂單可以繼續操作時,廚師長會抽調廚師繼續完成訂單,從而增加了餐館處理訂單的能力。而并行編程則是餐館開業的時候,告訴了廚師長,需要8個廚師;廚師長就帶來了相應數量的廚師來處理訂單。

    這就是這兩天,我對同步,異步和并行之間的感悟。如有不對,敬請指正!

    推薦閱讀:

    《Kubernetes全棧架構師(Kubeadm高可用安裝k8s集群)--學習筆記》

    《.NET 云原生架構師訓練營(模塊一 架構師與云原生)--學習筆記》

    《.NET Core開發實戰(第1課:課程介紹)--學習筆記》

    總結

    以上是生活随笔為你收集整理的关于async和await的探讨的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。