一、簡介
Async/Await在.Net Core中真的是無處不在,到處都是異步操作,那為什么要用?有什么作用?別人說能提升性能?網上一堆文章看的繞暈了也沒說清楚,
所以這里從理論,實踐,原理一個個解開這些疑問。
二、Async/Await有什么用?
1.Async/Await用法示例
用法很簡單,這里就不詳細說具體怎么用了,只提供一個示例,我們的目標是研究它的作用。
public class AsyncAwaitTest { public void Start() { Console.WriteLine($"aaa,線程Id:{Thread.CurrentThread.ManagedThreadId}"); AsyncMethod(); Console.WriteLine($"eee,線程Id:{Thread.CurrentThread.ManagedThreadId}"); Console.ReadLine(); } public async Task<bool> AsyncMethod() { Console.WriteLine($"bbb,線程Id:{Thread.CurrentThread.ManagedThreadId}"); await Task.Run(() => { Thread.Sleep(500); Console.WriteLine($"ccc,線程Id:{Thread.CurrentThread.ManagedThreadId}"); }); Console.WriteLine($"ddd,線程Id:{Thread.CurrentThread.ManagedThreadId}"); return true; } }
2.async/await的特點
1)不會阻塞線程
從示例的執行順序,可以看出,當執行async/await方法時,主線程遇到await關鍵字,主線程就返回執行“eee”,然后釋放,而不是等待,新開了一個子線程6執行另外的業務,await前面的方法還是主線程執行,await后面的方法,等線程6執行完了再繼續執行。
2)同步的方式寫異步
雖然是用了異步,但還是等待執行結果再往下執行,執行流程是同步的。
3.async/await能提升性能嗎?
這個應該是大家最關心的問題了。
能提升單個請求的性能嗎?
答案是不能的。很明顯看的到,await等待了結果再執行后面的邏輯,還是串行的,執行完該多少秒還是多少秒, 中間還切換線程去處理了,相比同步來說還多了切換線程的損耗。
那async/await的意義何在?
在於多請求並發處理,且資源有限的時候,能增加吞吐量(單位時間處理的請求),增加cpu的利用率。
簡單說就是有10個線程,每個線程的速度沒有提升,然后居然QPS能提升?!
先來看一段微軟官網的描述
此模型可很好地處理典型的服務器方案工作負荷。由於沒有專用於阻止未完成任務的線程,因此服務器線程池可服務更多的Web請求。
考慮使用兩個服務器:一個運行異步代碼,一個不運行異步代碼。對於本例,每個服務器只有5個線程可用於服務器請求。此字數太小,不切實際,僅供演示。
假設這兩個服務器都接收6個並發請求。每個請求執行一個I/O操作。未運行異步代碼的服務器必須對第6個請求排隊,直到5個線程中的一個完成了I/O密集型工作
並編寫了響應。此時收到了第20個請求,由於隊列過長,服務器可能會開始變慢。
運行有異步代碼的服務器也需要對第6個請求排隊,但由於使用了async和await,I/O密集型工作開始時,每個線程都會得到釋放,無需等到工作結束。
收到第20個請求時,傳入請求隊列將變得很小(如果其中還有請求的話),且服務器不會慢。
盡管這是一個人為想象的示例,但現實世界中其工作方式與此類似。事實上,相比服務器將線程專用於接收到的每個請求,使用async和await能夠使
服務器處理一個數量級的請求。
注意上面官網描述的I/O密集型。什么樣的是I/O密集型呢?,就是cpu性能比硬盤內存好太多,大部分時間都是cpu在等IO的讀寫操作。例如讀文件,讀文件的時候是不需要cpu參與的,只需要發一個命令給硬盤,硬盤讀完文件會再通知cpu繼續處理,這種叫DMA技術。
DMA(Direct Memory Access,直接存儲器訪問) 是所有現代電腦的重要特色,它是指一種高速的數據傳輸操作,允許在外部設備和存儲器之間直接讀寫數據,既不通過cpu,也不需要cpu干預。
這個時候異步就顯出它的優勢來了,比如讀文件需要1s,如果是同步操作,那么就有一個線程在等1s再往下執行。如果是異步的,讀文件的時候,這個線程就釋放了,等讀完文件,硬盤通知cpu再派一個線程接着處理,那中間的1秒,原來的線程就可以去處理其他請求了。
4.代碼對照說明
public class HomeController : Controller { /// <summary> /// 同步請求 /// </summary> /// <param name="path"></param> /// <returns></returns> public string GetData() { var result = System.IO.File.ReadAllBytes(@"F:\package\package.rar"); return "ok"; } /// <summary> /// 異步請求 /// </summary> /// <param name="path"></param> /// <returns></returns> public async Task<string> GetDataAsync2() { var result = await System.IO.File.ReadAllBytesAsync(@"F:\package\package.rar"); return "ok"; } }
同步請求的流程為
可以看出,硬盤在讀取文件時,線程是在等待的,這時候線程1在這1s中是不工作的,空等狀態。
異步請求的流程為
異步請求時,線程1遇到await關鍵字,發出命令就返回,然后釋放掉了,硬盤讀完數據會通知cpu,這時cpu派一個新的線程去接着處理,
因此,讀文件的這1s,線程1可以去處理其它請求了,沒有空等,這就是提高了cpu的利用率,單位時間內處理的請求數就變大了。
cpu密集型的異步是不能提高QPS的,下面代碼就是cpu密集型的。
cpu密集型:計算密集型,硬盤、內存性能比cpu好很多,或不太需要訪問I/O設備。
/// <summary> /// 異步請求 /// </summary> /// <param name="path"></param> /// <returns></returns> public async Task<string> GetDataAsync2() { await Task.Run(() => { Thread.Sleep(100);//模擬業務處理耗時 }); return "ok"; }
這里前面主線程遇到await雖然釋放了,但await里面又有一個線程接着工作,cpu(線程)並沒有空閑
I/O密集型中IO的操作有哪些呢?
文件讀寫、http請求、數據庫請求、redis請求。。。等等。
開發中哪些推薦用異步呢?
Web開發推薦、有Async的API的,Action、Filter、數據庫訪問、中間件等等。。。