C#多線程開發(fā)之任務(wù)并行庫(kù)詳解
前言
之前學(xué)習(xí)了線程池,知道了它有很多好處。
使用線程池可以使我們?cè)跍p少并行度花銷時(shí)節(jié)省操作系統(tǒng)資源??烧J(rèn)為線程池是一個(gè)抽象層,其向程序員隱藏了使用線程的細(xì)節(jié),使我們可以專心處理程序邏輯,而不是各種線程問題。
但也不是說我們所有的項(xiàng)目中都上線程池,其實(shí)它也有很多弊端,比如我們需要自定義使用異步委托的方式才可以將線程中的消息或異常傳遞出來。這些如果在一個(gè)大的軟件系統(tǒng)中,會(huì)導(dǎo)致軟件結(jié)構(gòu)過于混亂,各個(gè)線程之間消息傳遞來傳遞去的,如果發(fā)生沒有處理掉的異常,很容易導(dǎo)致軟件出現(xiàn)致命錯(cuò)誤。
為了解決這個(gè)問題,在.Net Framework 4.0中引入了一個(gè)新的異步操作的API,它叫任務(wù)并行庫(kù)(TPL)。
那么接下來,讓我們一起來認(rèn)識(shí)一下這個(gè)TPL,看看它到底有什么魔力可以把線程池中的棘手問題解決掉。
任務(wù)并行庫(kù)
TPL又被認(rèn)為是線程池的有一個(gè)抽象,其對(duì)程序員隱藏了線程池交互的底層代碼,并只提供了更方便的細(xì)粒度的API。
TPL的核心是任務(wù)。一個(gè)任務(wù)代表一個(gè)異步操作,該操作可以通過多種方式運(yùn)行,可以使用或不使用獨(dú)立線程運(yùn)行。
TPL有一個(gè)關(guān)鍵優(yōu)勢(shì),就是一個(gè)任務(wù)可以通過多種方式和其它任務(wù)組合起來。
比如可以同時(shí)開啟多個(gè)任務(wù),等待所有任務(wù)完成,然后運(yùn)行一個(gè)任務(wù)對(duì)之前所有任務(wù)的結(jié)果進(jìn)行一些計(jì)算。
可以使用AggregateException來捕獲底層任務(wù)內(nèi)部所有異常,并允許單獨(dú)處理這些異常。在C#5.0中已經(jīng)內(nèi)置了對(duì)TPL的支持,允許我們使用心得await和async關(guān)鍵字以平滑的、舒服的方式操作任務(wù)。
一、創(chuàng)建任務(wù)
可以通過下面三種方式來創(chuàng)建任務(wù)。
var a1 = new Task(()=>TastMethod("線程01")); a1.Start(); Task.Run(()=>TastMethod("線程001")); //已棄用 Task.Factory.StartNew(()=>TastMethod("線程02")); Task.Factory.StartNew(() => TastMethod("線程03"),TaskCreationOptions.LongRunning); Console.ReadKey();
在最新的.NET 5.0中已經(jīng)將任務(wù)快速啟動(dòng)方式Run,丟棄掉了。只能使用其余的兩種。實(shí)例化的Tast屬性,必須進(jìn)行啟動(dòng),任務(wù)才可以執(zhí)行。其余的.NET已經(jīng)做了內(nèi)置,只需要使用就默認(rèn)自動(dòng)開啟。
在線程3開啟過程中,增加了TaskCreationOptions.LongRuning參數(shù),它表示標(biāo)記該任務(wù)為長(zhǎng)時(shí)間運(yùn)行,結(jié)果該任務(wù)將不會(huì)使用線程池,而在單獨(dú)的線程中運(yùn)行。然而根據(jù)運(yùn)行該任務(wù)的當(dāng)前任務(wù)調(diào)度程序,運(yùn)行方式可能不同。
二、使用任務(wù)執(zhí)行基本操作
下面介紹下從任務(wù)中得到其計(jì)算法返回的結(jié)果。
static void Main(string[] args) { var a1 = new Task<int>(()=>TastMethod("線程01")); a1.Start(); int result = a1.Result; Console.WriteLine("result:" + result); Console.ReadKey(); } static int TastMethod(string name) { Console.WriteLine("線程名字:"+name+"Id:"+Thread.CurrentThread.ManagedThreadId+"是否屬于線程池:"+Thread.CurrentThread.IsThreadPoolThread); return 40; }
輸出結(jié)果
這里我們聲明并運(yùn)行了線程01并等待結(jié)果,該任務(wù)會(huì)被放置在線程池中,并且主線程會(huì)等待,直到任務(wù)返回前一直處于阻塞狀態(tài)。
其實(shí)也可以調(diào)用方法RunSynchronously()方法,使其特定運(yùn)行在主線程。這是一個(gè)非常好的優(yōu)化,可以避免使用線程池來執(zhí)行非常短暫的操作。
三、處理任務(wù)中的異常
在異步任務(wù)中,對(duì)于異常的處理是非常重要的。
try { var a1 = new Task<int>(() => TastMethod("線程01",2)); a1.Start(); int result = a1.Result; Console.WriteLine("result:" + result); } catch (Exception ex) { Console.WriteLine(ex.Message); }
當(dāng)程序啟動(dòng)時(shí),創(chuàng)建了一個(gè)任務(wù)并嘗試同步獲取任務(wù)結(jié)果。Result屬性的Get部分會(huì)使當(dāng)前線程等待直到該任務(wù)結(jié)束,并將異常傳播給當(dāng)前線程。此時(shí)通過try/catch是很容易捕獲到的(需要注意AggregateExceptiont,它被封裝起來,)。
int result = a1.GetAwaiter().GetResult ;
上面這種情況無需封裝異常,可以使用GetAwaiter和GetResult方法來訪問任務(wù)結(jié)果。
總結(jié)
到此這篇關(guān)于C#多線程開發(fā)之任務(wù)并行庫(kù)的文章就介紹到這了,更多相關(guān)C#任務(wù)并行庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# 使用Microsoft Edge WebView2的相關(guān)總結(jié)
這篇文章主要介紹了C# 使用Microsoft Edge WebView2的相關(guān)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-02-02C#在Unity游戲開發(fā)中進(jìn)行多線程編程的方法
這篇文章主要介紹了C#在Unity游戲開發(fā)中進(jìn)行多線程編程的方法,文中總結(jié)了Unity中使用多線程的幾種方式以及一款多線程插件的介紹,需要的朋友可以參考下2016-04-04C#開發(fā)windows服務(wù)實(shí)現(xiàn)自動(dòng)從FTP服務(wù)器下載文件
這篇文章主要為大家詳細(xì)介紹了C#開發(fā)windows服務(wù)實(shí)現(xiàn)自動(dòng)從FTP服務(wù)器下載文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03c# 如何對(duì)網(wǎng)絡(luò)信息進(jìn)行相關(guān)設(shè)置(ip,dns,網(wǎng)關(guān)等)
這篇文章主要介紹了c# 網(wǎng)絡(luò)適配器的相關(guān)操作,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-03-03Unity實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)倒計(jì)時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05C#設(shè)計(jì)模式之建造者模式生成器模式示例詳解
這篇文章主要為大家介紹了C#設(shè)計(jì)模式之建造者模式生成器模式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08