国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

C#執(zhí)行異步操作的幾種方式比較和總結(jié)

2019-11-14 13:58:08
字體:
供稿:網(wǎng)友

C#執(zhí)行異步操作的幾種方式比較和總結(jié)

0x00 引言

之前寫程序的時(shí)候在遇到一些比較花時(shí)間的操作例如HTTP請求時(shí),總是會(huì)new一個(gè)Thread處理。對XxxxxAsync()之類的方法也沒去了解過,倒也沒遇到什么大問題。最近因?yàn)樾枨笠笥肈evExPRess寫界面,跑起來后發(fā)現(xiàn)比Native控件效率差好多。這才想到之前看到的“金科玉律”:不要在UI線程上執(zhí)行界面無關(guān)的操作,因此集中看了下C#的異步操作,分享一下自己的比較和總結(jié)。

0x01 測試方法

IDE:VS2015 Community

.NET版本:4.5

使用函數(shù)隨機(jī)休眠100到500毫秒來模擬耗時(shí)任務(wù),并返回任務(wù)花費(fèi)的時(shí)間,在UI線程上調(diào)用這個(gè)方法會(huì)造成阻塞,導(dǎo)致UI假死,因此需要通過異步方式執(zhí)行這個(gè)任務(wù),并在信息輸出區(qū)域顯示花費(fèi)的時(shí)間。

 

主界面中通過各種不同按鈕測試不同類型的異步操作

 

0x02 使用Thread進(jìn)行異步操作

使用ThreadPool進(jìn)行異步操作的方法如下所示,需要注意的就是IsBackground默認(rèn)為false,也就是該線程對調(diào)用它的線程不產(chǎn)生依賴,當(dāng)調(diào)用線程退出時(shí)該線程也不會(huì)結(jié)束。因此需要將IsBackground設(shè)置為true以指明該線程是后臺(tái)線程,這樣當(dāng)主線程退出時(shí)該線程也會(huì)結(jié)束。另外跨線程操作UI還是要借助Dispatcher.BeginInvoke(),如果需要阻塞UI線程可以使用Dispatcher.Invoke()。

 

0x03 使用ThreadPool進(jìn)行異步操作

ThreadPool(線程池)的出現(xiàn)主要就是為了提高線程的復(fù)用(類似的還有訪問數(shù)據(jù)庫的連接池)。線程的創(chuàng)建是開銷比較大的行為,為了達(dá)到較好的交互體驗(yàn),開發(fā)中可能會(huì)大量使用異步操作,特別是需要頻繁進(jìn)行大量的短時(shí)間的異步操作時(shí),頻繁創(chuàng)建和銷毀線程會(huì)在造成很多資源的浪費(fèi)。而通過在線程池中存放一些線程,當(dāng)需要新建線程執(zhí)行操作時(shí)就從線程池中取出一個(gè)已經(jīng)存在的空閑線程使用,如果此時(shí)沒有空閑線程,且線程池中的線程數(shù)未達(dá)到線程池上限,則新建一個(gè)線程,使用完成后再放回到線程池中。這樣可以極大程度上省去線程創(chuàng)建的開銷。線程池中線程的最小和最大數(shù)都可以指定,不過多數(shù)情況下無需指定,CLR有一套管理線程池的策略。ThreadPool的使用非常簡單,代碼如下所示。跨線程操作UI仍需借助Dispatcher。

 

0x04 使用Task進(jìn)行異步操作

Task進(jìn)行異步操作時(shí)也是從線程池中獲取線程進(jìn)行操作,不過支持的操作更加豐富一些。而且Task<T>可以支持返回值,通過Task的ContinueWith()可以在Task執(zhí)行結(jié)束后將返回值傳入以進(jìn)行操作,但在ContinueWith中跨線程操作UI仍需借助Dispatcher。另外Task也可以直接使用靜態(tài)方法Task.Run<T>()執(zhí)行異步操作。

 

0x05 使用async/await進(jìn)行異步操作

這個(gè)是C#5中的新特性,當(dāng)遇到await時(shí),會(huì)從線程池中取出一個(gè)線程異步執(zhí)行await等待的操作,然后方法立即返回。等異步操作結(jié)束后回到await所在的地方接著往后執(zhí)行。await需要等待async Task<T>類型的函數(shù)。詳細(xì)的使用方法可參考相關(guān)資料,測試代碼如下所示。異步結(jié)束后的會(huì)返回到調(diào)用線程,所以修改UI不需要Dispatcher。

 

也可以把TestTask包裝成async方法,這樣就可以使用上圖中注釋掉的兩行代碼進(jìn)行處理。包裝后的異步方法如下所示:

 

async/await也是從線程池中取線程,可實(shí)現(xiàn)線程復(fù)用,而且代碼簡潔容易閱讀,異步操作返回后會(huì)自動(dòng)返回調(diào)用線程,是執(zhí)行異步操作的首選方式。而且雖然是C#5的新特性,但C#4可以通過下載升級(jí)包來支持async/await。

0x06 關(guān)于效率

以上嘗試的方法除了直接使用Thread之外,其他幾種都是直接或間接使用線程池來獲取線程的。從理論上來分析,創(chuàng)建線程時(shí)要給線程分配棧空間,線程銷毀時(shí)需要回收內(nèi)存,創(chuàng)建線程也會(huì)增加CPU的工作。因此可以連續(xù)創(chuàng)建線程并記錄消耗的時(shí)間來測試性能。測試代碼如下所示:

 

當(dāng)測試Thread時(shí)每次測試在連續(xù)創(chuàng)建線程時(shí)內(nèi)存和CPU都會(huì)有個(gè)小突起,不過在線程結(jié)束后很快就會(huì)降下去,在我的電腦上連續(xù)創(chuàng)建100個(gè)線程大概花費(fèi)120-130毫秒。如圖所示:

 

測試結(jié)果:

 

使用基于線程池的方法創(chuàng)建線程時(shí),有時(shí)第一次會(huì)稍慢一些,應(yīng)該是線程池內(nèi)線程不足,時(shí)間開銷在0-15毫秒,第一次創(chuàng)建內(nèi)存也會(huì)上升。后面再測試時(shí)時(shí)間開銷為0毫秒,內(nèi)存表現(xiàn)也很平穩(wěn),CPU開銷分布比較平均。測試結(jié)果如圖所示:

 

0x07 結(jié)論

在執(zhí)行異步操作時(shí)應(yīng)使用基于線程池的操作,從代碼的簡潔程度和可讀性上優(yōu)先使用async/await方式。對于較老的.NET版本可以使用Task或ThreadPool。符合以下情況的可以使用Thread:

1、線程創(chuàng)建后需要持續(xù)工作到主線程退出的。這種情況下就算使用線程池線程也不會(huì)歸還,實(shí)現(xiàn)不了復(fù)用,可以使用Thread。

2、線程在主線程退出后仍需要執(zhí)行的,這種情況使用線程池線程無法滿足需求,需要使用Thread并制定IsBackground為false(默認(rèn))。

0x08 相關(guān)下載

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

 


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 威宁| 宁津县| 无棣县| 黄大仙区| 东平县| 泗水县| 辛集市| 彩票| 靖远县| 石台县| 黄冈市| 丰原市| 内丘县| 敦煌市| 乌拉特后旗| 中山市| 万载县| 诸暨市| 永嘉县| 江源县| 禹城市| 平果县| 敖汉旗| 铜梁县| 射阳县| 黄龙县| 东乌| 平舆县| 崇信县| 曲靖市| 同心县| 萝北县| 仙游县| 新密市| 海口市| 杨浦区| 沙洋县| 什邡市| 广西| 旬邑县| 比如县|