使用Task異步執行方法_多線程_應用程序池

偶然遇到在執行登陸的方法須要發送消息隊列致使登陸時間過長的問題,從網上查了一些方法,先將一個簡單的異步處理程序的小例子展現出來,供你們參考:html

備註:該方法是從應用程序程序所在的線程池中獲取線程,第一次建立異步線程可能會費用一些時間,建立後不會當即銷燬,以便其餘線程屢次重複使用,對程序性能影響不大:git

//異步執行修改用戶登陸時間併發送隊列--20171209-lixh
Task.Run(() =>
{
    UserInfoBIZ.UpdateUserInfo(userInfo, ConfigSetting.SITEID, ConfigSetting.WEB_SITE_NAME, EPlatform.PC端);
});

 

 

本文轉載地址:http://www.javashuo.com/article/p-mbonqvcu-x.htmlgithub

如下是轉載全文:數據庫

0x00 引言

以前寫程序的時候在遇到一些比較花時間的操做例如HTTP請求時,老是會new一個Thread處理。對XxxxxAsync()之類的方法也沒去了解過,倒也沒遇到什麼大問題。最近由於需求要求用DevExpress寫界面,跑起來後發現比Native控件效率差好多。這纔想到以前看到的「金科玉律」:不要在UI線程上執行界面無關的操做,所以集中看了下C#的異步操做,分享一下本身的比較和總結。併發

0x01 測試方法

IDE:VS2015 Community異步

.NET版本:4.5async

使用函數隨機休眠100到500毫秒來模擬耗時任務,並返回任務花費的時間,在UI線程上調用這個方法會形成阻塞,致使UI假死,所以須要經過異步方式執行這個任務,並在信息輸出區域顯示花費的時間。函數

 

主界面中經過各類不一樣按鈕測試不一樣類型的異步操做性能

 

0x02 使用Thread進行異步操做

使用ThreadPool進行異步操做的方法以下所示,須要注意的就是IsBackground默認爲false,也就是該線程對調用它的線程不產生依賴,當調用線程退出時該線程也不會結束。所以須要將IsBackground設置爲true以指明該線程是後臺線程,這樣當主線程退出時該線程也會結束。另外跨線程操做UI仍是要藉助Dispatcher.BeginInvoke(),若是須要阻塞UI線程可使用Dispatcher.Invoke()。測試

 

0x03 使用ThreadPool進行異步操做

ThreadPool(線程池)的出現主要就是爲了提升線程的複用(相似的還有訪問數據庫的鏈接池)。線程的建立是開銷比較大的行爲,爲了達到較好的交互體驗,開發中可能會大量使用異步操做,特別是須要頻繁進行大量的短期的異步操做時,頻繁建立和銷燬線程會在形成不少資源的浪費。而經過在線程池中存放一些線程,當須要新建線程執行操做時就從線程池中取出一個已經存在的空閒線程使用,若是此時沒有空閒線程,且線程池中的線程數未達到線程池上限,則新建一個線程,使用完成後再放回到線程池中。這樣能夠極大程度上省去線程建立的開銷。線程池中線程的最小和最大數均可以指定,不過多數狀況下無需指定,CLR有一套管理線程池的策略。ThreadPool的使用很是簡單,代碼以下所示。跨線程操做UI仍需藉助Dispatcher。

 

0x04 使用Task進行異步操做

Task進行異步操做時也是從線程池中獲取線程進行操做,不過支持的操做更加豐富一些。並且Task<T>能夠支持返回值,經過Task的ContinueWith()能夠在Task執行結束後將返回值傳入以進行操做,但在ContinueWith中跨線程操做UI仍需藉助Dispatcher。另外Task也能夠直接使用靜態方法Task.Run<T>()執行異步操做。

 

0x05 使用async/await進行異步操做

這個是C#5中的新特性,當遇到await時,會從線程池中取出一個線程異步執行await等待的操做,而後方法當即返回。等異步操做結束後回到await所在的地方接着日後執行。await須要等待async Task<T>類型的函數。詳細的使用方法可參考相關資料,測試代碼以下所示。異步結束後的會返回到調用線程,因此修改UI不須要Dispatcher。

 

也能夠把TestTask包裝成async方法,這樣就可使用上圖中註釋掉的兩行代碼進行處理。包裝後的異步方法以下所示:

 

async/await也是從線程池中取線程,可實現線程複用,並且代碼簡潔容易閱讀,異步操做返回後會自動返回調用線程,是執行異步操做的首選方式。並且雖然是C#5的新特性,但C#4能夠經過下載升級包來支持async/await。

0x06 關於效率

以上嘗試的方法除了直接使用Thread以外,其餘幾種都是直接或間接使用線程池來獲取線程的。從理論上來分析,建立線程時要給線程分配棧空間,線程銷燬時須要回收內存,建立線程也會增長CPU的工做。所以能夠連續建立線程並記錄消耗的時間來測試性能。測試代碼以下所示:

 

當測試Thread時每次測試在連續建立線程時內存和CPU都會有個小突起,不過在線程結束後很快就會降下去,在個人電腦上連續建立100個線程大概花費120-130毫秒。如圖所示:

 

測試結果:

 

使用基於線程池的方法建立線程時,有時第一次會稍慢一些,應該是線程池內線程不足,時間開銷在0-15毫秒,第一次建立內存也會上升。後面再測試時時間開銷爲0毫秒,內存表現也很平穩,CPU開銷分佈比較平均。測試結果如圖所示:

 

0x07 結論

在執行異步操做時應使用基於線程池的操做,從代碼的簡潔程度和可讀性上優先使用async/await方式。對於較老的.NET版本可使用Task或ThreadPool。符合如下狀況的可使用Thread:

一、線程建立後須要持續工做到主線程退出的。這種狀況下就算使用線程池線程也不會歸還,實現不了複用,可使用Thread。

二、線程在主線程退出後仍須要執行的,這種狀況使用線程池線程沒法知足需求,須要使用Thread並制定IsBackground爲false(默認)。

0x08 相關下載

測試程序代碼在:https://github.com/durow/TestArea/tree/master/AsyncTest/AsyncTest

相關文章
相關標籤/搜索