注冊 | 登陸
您的位置:阿里西西 > 編程技術 > .NET教程 > 詳細內容

對 ASP.NET 異步編程的一點理解與分析

  稿源:本站整理   2018-09-06   點擊:   撤稿糾錯
如果你的應用程序請求訪問很少(并發很小),異步和同步將是一樣的效 果,異步化改造是毫無意義的,而如果你的應用程序請求訪問很多(并發很大),那么效果顯而易見,如果使用異步將會為你省掉幾臺服務器的錢,但代碼異步化并 不能使你的應用程序執行速度加快(指的是代碼執行速度),垃圾代碼還是垃圾代碼,并不會有任何的改善,所以,寫好“好的代碼”很重要!!!

阿里西西web開發網為大家整理了這篇對 ASP.NET 異步編程的一點理解與分析,如果這篇文章在您的工作或學習中有幫助,歡迎常回來看看哦,更多精彩的教程請訪問我們的主頁,以下是教程瀏覽:

本來這篇博文想探討下異步中的異常操作,但自己在做異步測試的時候,又對 ASP.NET 異步有了新的認識,可以說自己之前對異步的理解還是有些問題,先列一下這篇博文的三個解惑點:

  • async await 到底是什么鬼???

  • 異步操作中發生異常,該如何處理?

  • 異步操作中發生異常(有無 catch throw 情況),Application_Error 會不會捕獲?

之前測試過異步中的同步(很多種情況),這次我們把測試代碼寫更復雜些(異步中再進行異步),代碼如下:


 
  1. [Route("")] 
  2. [HttpGet] 
  3. public async Task<string> Index() 
  4.     System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); 
  5.     var result = await Test(); 
  6.     System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId6:" + Thread.CurrentThread.ManagedThreadId); 
  7.     return result; 
  8.  
  9. public static async Task<string> Test() 
  10.     System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); 
  11.     using (var client = new HttpClient()) 
  12.     { 
  13.         var response = await client.GetAsync("http://stackoverflow.com/questions/14996529/why-is-my-async-asp-net-web-api-controller-blocking-the-main-thread"); 
  14.         await Test2(); 
  15.         System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId5:" + Thread.CurrentThread.ManagedThreadId); 
  16.         return await response.Content.ReadAsStringAsync(); 
  17.     } 
  18.  
  19. public static async Task<string> Test2() 
  20.     System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); 
  21.     using (var client = new HttpClient()) 
  22.     { 
  23.         var response = await client.GetAsync("http://stackoverflow.com/questions/33408905/pgadminiii-bug-on-query-tool"); 
  24.         System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); 
  25.         return await response.Content.ReadAsStringAsync(); 
  26.     } 
  27.  
  28. 輸出結果(執行四次): 
  29.  
  30. Thread.CurrentThread.ManagedThreadId1:8 
  31. Thread.CurrentThread.ManagedThreadId2:8 
  32. Thread.CurrentThread.ManagedThreadId3:6 
  33. Thread.CurrentThread.ManagedThreadId4:6 
  34. Thread.CurrentThread.ManagedThreadId5:6 
  35. Thread.CurrentThread.ManagedThreadId6:6 
  36.  
  37. Thread.CurrentThread.ManagedThreadId1:7 
  38. Thread.CurrentThread.ManagedThreadId2:7 
  39. Thread.CurrentThread.ManagedThreadId3:8 
  40. Thread.CurrentThread.ManagedThreadId4:7 
  41. Thread.CurrentThread.ManagedThreadId5:7 
  42. Thread.CurrentThread.ManagedThreadId6:7 
  43.  
  44. Thread.CurrentThread.ManagedThreadId1:5 
  45. Thread.CurrentThread.ManagedThreadId2:5 
  46. Thread.CurrentThread.ManagedThreadId3:5 
  47. Thread.CurrentThread.ManagedThreadId4:6 
  48. Thread.CurrentThread.ManagedThreadId5:6 
  49. Thread.CurrentThread.ManagedThreadId6:6 
  50.  
  51. Thread.CurrentThread.ManagedThreadId1:8 
  52. Thread.CurrentThread.ManagedThreadId2:8 
  53. Thread.CurrentThread.ManagedThreadId3:8 
  54. Thread.CurrentThread.ManagedThreadId4:8 
  55. Thread.CurrentThread.ManagedThreadId5:8 
  56. Thread.CurrentThread.ManagedThreadId6:8 

這個測試方法,我執行了無數次,大致就是上面的四種情況,我當時看到輸出結果,其實是很凌亂的,我也大家也一樣,并心里有一些疑問:你這真是異步編程嗎?為啥線程千奇百怪?并且最后那個還只有一個線程,這和同步有啥區別???

針對上面這個疑問,我想了很久,并對自己產生了一些質疑的聲音:你每天都在寫 async await 代碼,你真的了解它嗎???然后我又重新找到上面那篇 jesse liu 的博文,反復讀了很多篇,最后終于有了一些“頓悟”,結合上面的測試代碼,我大致畫了一張示意圖:

結合上面的圖,我說一下自己的理解,在做測試的時候,HttpClient.GetAsync 盡量讓它執行時間長些,比如請求的 URL 可以是 stackoverflow 或 github(原因你懂得!),因為有個時間差,這樣我們可以更好的了解線程的執行情況,上面圖中“線程1、線程1x、線程3x、線程4x”等等,這些并 不是不同線程,也就是說線程1有可能等于線程1x或線程3x。。。從上面的輸出結果就可以看出,用線程x來表示兩個輸出之間所經歷的 await 次數,這就證明了一個疑惑:await 并不一定會創建和之前不一樣的線程。

到底什么是異步???我個人覺得,async 異步是一個偽概念,await 等待才是精髓,一個線程可以響應 多個請求,如果是同步編程,一個線程在處理某一個請求的時候阻塞了(比如上面測試代碼中的 HttpClient.GetAsync 網絡操作),那么這個線程就會一直等待它處理,在這個等待的過程中,那么其他請求就不能再使用這個線程,又因為 IIS 線程池中的線程數量有限,那么同步編程下,高并發將是一個頭疼的問題,試想一下,如果線程池中的線程數量為 100 個,這 100 個線程在同時處理 100 個請求的時候,都悲催的阻塞掉了,這時候第 101 個請求將無法執行,那么并發量就是 100。

接上面,同樣的處理過程,如果是異步編程,那將是什么情況呢?比如一個線程在處理某一個請求的時候,執行到 await 操作,那么這個線程將會釋放回到線程池,然后進行等待,等待的過程中,原來的那個線程就可以處理其他請求或者這個請求的其他操作,注意等待并不是線程等 待,而是操作等待,我原來就很不理解這個地方,如果是線程等待,就表示這個線程會一直等待它完成,那和同步編程就是一樣的了,所以這種理解是錯誤的,你可 以這樣理解:await 等待的過程中,沒有線程!!!

再接上面,等待操作完成之后,這時候就會從線程池中隨機拿一個線程繼續執行,拿到的這個線程有可能是 await 操作剛剛釋放掉的,但也有可能是其他線程,上圖中的 2-6 操作就是這樣,一圖勝千言:

了解了整個過程之后,你才會明白 async await 到底是什么鬼?以及它真正的用武之地是什么?簡單總結幾點內容:

  • async 異步網絡處理作用最明顯(HttpClient 請求或數據庫連接):這個我們大家都很清楚,也 很好理解,如果是其他操作,比如一個異步方法中你做了很多費時的計算,那這個異步將沒什么效果,說白了和同步一樣,而對于網絡操作,我們一般不做處理,發 起請求之后等待它完成就行,所以這時候執行到這的線程,可以釋放并會到線程池中,網絡操作執行完成之后,再從線程池中隨機拿一個線程繼續執行。

  • async 異步并不是真正意義上的“異步”:什么意思呢?你仔細看下上面測試的輸出結果,會發現 ManagedThreadId1-6 是順序輸出的,而不是先輸出 ManagedThreadId4 再輸出 ManagedThreadID3,所以,異步和同步的執行過程是一樣的,并且一個請求下,執行時間也是一樣的,上面的異步測試其實某種意義上,是測試不出任何東西的(從測試結果就可以看出),異步并不能減少你的執行時間,而是增加你的請求執行數量,這個東西說白了,其實就是并發量。

  • async 異步的精髓是 await:這個之前已經提到了,準確來說,async 異步的精髓是 await 時的線程回收與完成之后的線程切換,這個操作最大的價值是,避免線程的浪費等待,充分利用線程的執行,有點類似于地主不能容忍奴隸閑著做無意義的事,而是希望他們 24 小時不停工作一樣。

另外,在 ASP.NET 應用程序中,我們可以使用 Thread.CurrentThread 來訪問當前的執行線程,我之前想做這樣一個測試,讓當前執行線程 Sleep 一段時間,看看其他線程會不會執行,但 Thread.CurrentThread 并沒有 Sleep 方法,而必須這樣訪問 Thread.Sleep(int millisecondsTimeout),如果這樣執行這段代碼,那么當前線程將會 Sleep,但其他線程并不會在它 Sleep 的時候,而繼續執行,為什么?因為 CPU 在同一時間段內只能執行一個線程。

了解了 async await 到底是什么鬼后,博文一開始剩下的兩個有關異步操作中的異常問題,現在理解起來就非常容易了:

  • 異步操作中發生異常,該如何處理?:和同步一樣處理,同步中報錯,異步也一樣報錯,有人可能有這樣的疑問,比 如測試代碼中的 Index Action,執行到 await Test 內部操作的時候,突然拋出異常了,然后就想當然的認為,既然是異步執行的 Test 方法,那 Index 應該不會影響吧?其實你執行之后就會發現,Index 頁面還是會拋出異常的,所以異常和異步沒半毛錢關系

  • 異步操作中發生異常(有無 catch throw 情況),Application_Error 會不會捕獲?:無 catch,Application_Error 會捕獲;有 catch 無 throw,Application_Error 不會捕獲;有 catch 有 throw,Application_Error 會捕獲。

如果我們想讓某一個異步方法,在執行拋出異常的時候,而不影響其他異步方法,那我們就 catch 而不 throw,比如我們的測試代碼:


 
  1. [Route("")] 
  2. [HttpGet] 
  3. public async Task<string> Index() 
  4.     System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); 
  5.     var result = await Test(); 
  6.     System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId6:" + Thread.CurrentThread.ManagedThreadId); 
  7.     return result; 
  8.  
  9. public static async Task<string> Test() 
  10.     try 
  11.     { 
  12.         System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); 
  13.         using (var client = new HttpClient()) 
  14.         { 
  15.             var response = await client.GetAsync("http://stackoverflow.com/questions/33408905/pgadminiii-bug-on-query-tool"); 
  16.             System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); 
  17.             throw new Exception("test exception");//這里出現了異常 
  18.             return await response.Content.ReadAsStringAsync(); 
  19.         } 
  20.     } 
  21.     catch (Exception ex) 
  22.     { 
  23.         System.Diagnostics.Debug.WriteLine("異常信息:" + ex.Message); 
  24.         return ""
  25.         //throw ex; 
  26.     } 

這樣的效果就是 Index 頁面不會報錯,并且也不會影響其他方法執行,現在發現當時疑惑這個問題的時候,還蠻白癡的,還是那句話,異常和異步沒半毛錢關系,相同的問題,同步也是這樣進行處理的。

博文內容有點多,如果你不愿花時間看,可以直接記住這段話:如果你的應用程序請求訪問很少(并發很小),異步和同步將是一樣的效 果,異步化改造是毫無意義的,而如果你的應用程序請求訪問很多(并發很大),那么效果顯而易見,如果使用異步將會為你省掉幾臺服務器的錢,但代碼異步化并 不能使你的應用程序執行速度加快(指的是代碼執行速度),垃圾代碼還是垃圾代碼,并不會有任何的改善,所以,寫好“好的代碼”很重要!!!


關于對 ASP.NET 異步編程的一點理解與分析的內容寫到這里就結束啦,您可以收藏本頁網址http://www.kimling.cn/biancheng/a/2018090695562.shtml方便下次再訪問哦。


更多關于 異步 編程 的文章
相關閱讀

相關排行總榜

ASP教程

PHP教程

.NET教程

ASP采集功能WinHttp.WinHttpRequest.5.1的
AspJpeg圖片處理組件屬性的詳細中文使用說
通過ASP生成html純靜態頁面的簡單示例
ASP開發中一個把GB2312轉UTF-8編碼的函數
推薦幾個ASP開發中常用的fso操作文件圖片
ASP讀取操作生成excel文件的實例代碼教程
使用ASP進行網站開發防范ASP木馬的10條建
通過ASP調用MSSQL數據庫視圖和存儲過程的
通過aspupload上傳組件實現的ASP實現遠程
ASP調用MSSQL存儲過程進行分頁的兩種方式
PHP解決HTTP和HTTPS跨域共享session的方法
php程序判斷來訪客戶端是手機端還是電腦P
用PHP來獲取當前頁面所有url參數信息的教
配置好環境后,進行php代碼調試的方法總結
教您使用php代碼實現上傳圖片或文件到服務
PHP開發中推薦十款最出色的安全開發工具庫
PHP基礎學習之數組介紹
淺談JSP與ASP.PHP的各種平臺應用比較
php制作一個萬年歷查詢的實例代碼教程
制作簡單安全的php驗證碼類代碼實例
ASP.NET環境配置中幾種身份驗證方式及權限
記錄一次對ASP.NET網站漏洞的入侵和防范教
Asp.net在mvc環境下實現上傳頭像加剪裁功
對 ASP.NET 異步編程的一點理解與分析
ASP.NET讀取與操作Session的代碼案例教程
ASP.NET將大文件數據分成較小的部分進行分
講解.Net組件程序設計之異步調用
AS腳本與Asp.net通過UrlRequest進行前端交
一組常用的.NET命名空間中文解釋
asp.net解決url地址欄傳遞中文參數出現亂
吸奶头吸到高潮视频免费视频,中文字幕无码第1页,里番高H无码无修在线观看,99热成人精品国产免