一、簡介
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、資料庫訪問、中介軟體等等。。。