async/await 是我們在 ASP.NET 應用程序中,寫異步代碼最常用的兩個關鍵字,使用它倆,我們不需要考慮太多背后的東西,比如異步的原理等等,如果你的 ASP.NET 應用程序是異步到底的,包含數據庫訪問異步、網絡訪問異步、服務調用異步等等,那么恭喜你,你的應用程序是沒問題的,但有一種情況是,你的應用程序代碼比較老,是同步的,但現在你需要調用異步代碼,這該怎么辦呢?有人可能會說,很簡單啊,不是有個 .Result 嗎?但事實真的就這么簡單嗎?我們來探究下。
首先,放出幾篇經典文章:
上面文章的內容,我們后面會說。光看不練假把式,所以,如果真正要體會 sync over async,我們還需要自己動手進行測試:
先說明一下,在測試代碼中,異步調用使用的是 HttpClient.GetAsync 方法,并且測試請求執行兩次,關于具體的分析,后面再進行說明。
測試代碼:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test(); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static string Test(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var response = client.GetAsync(url).Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return response.Content.ReadAsStringAsync().Result; }}
輸出結果:
Thread.CurrentThread.ManagedThreadId1:13Thread.CurrentThread.ManagedThreadId2:13Thread.CurrentThread.ManagedThreadId3:13Thread.CurrentThread.ManagedThreadId4:13Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:6Thread.CurrentThread.ManagedThreadId3:6Thread.CurrentThread.ManagedThreadId4:6
簡單總結:同步代碼中調用異步,上面的測試代碼應該是我們最常寫的,為什么沒有出現線程阻塞,頁面卡死的情況呢?而且代碼中調用了 GetAsync,為什么請求線程只有一個?后面再說,我們接著測試。
測試代碼:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Task.Run(() => Test2()).Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test2(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var response = await client.GetAsync(url); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return await response.Content.ReadAsStringAsync(); }}
輸出結果:
Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:7Thread.CurrentThread.ManagedThreadId3:11Thread.CurrentThread.ManagedThreadId4:6Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:7Thread.CurrentThread.ManagedThreadId3:12Thread.CurrentThread.ManagedThreadId4:6
簡單總結:根據上面的輸出結果,我們發現,在一個請求過程中,總共會出現三個線程,一個是開始的請求線程,接著是 Task.Run 創建的一個線程,然后是異步方法中 await 等待的執行線程,需要注意的是,ManagedThreadId1 和 ManagedThreadId4 始終是一樣的。
測試代碼:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test3().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test3(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var response = await client.GetAsync(url); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return await response.Content.ReadAsStringAsync(); }}
輸出結果:
Thread.CurrentThread.ManagedThreadId1:5Thread.CurrentThread.ManagedThreadId2:5
簡單總結:首先,頁面是卡死狀態,ManagedThreadId3 并沒有輸出,也就是執行到 await client.GetAsync
的時候,線程就阻塞了。
測試代碼:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test4().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test4(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); return await Task.Run(() => { Thread.Sleep(1000); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return "xishuai"; });}
輸出結果:
Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:6Thread.CurrentThread.ManagedThreadId3:7
簡單總結:和第三種情況一樣,頁面也是卡死狀態,但不同的是,ManagedThreadId3 是輸出的,測試它的主要目的是和第三種情況形成對比,以便了解 HttpClient.GetAsync
中到底是什么鬼?
測試代碼:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test5().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test5(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var task = client.GetAsync(url); var response = await task.ConfigureAwait(true); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return await response.Content.ReadAsStringAsync(); }}
輸出結果:
Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:6
簡單總結:和上面兩種情況一樣,頁面也是卡死狀態,它的效果和第三種完全一樣,ManagedThreadId3 都沒有輸出的。
測試代碼:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test6().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}publ
新聞熱點
疑難解答