C#并發(fā)編程之Task類詳解
Task.Run
Task是建立在線程池之上的一種多線程技術(shù),它的出現(xiàn)使Thread成為歷史。其使用方法非常簡(jiǎn)單,下面在頂級(jí)語(yǔ)句中做一個(gè)簡(jiǎn)單的例子
void printN(string name) { for (int i = 0; i < 3; i++) { Console.WriteLine($"{name}:{i}"); Task.Delay(1000).Wait(); } } Task.Run(() => printN("t1")); Task.Run(() => printN("t2")); Task.Run(() => printN("t3")).Wait();
運(yùn)行后,命令行中的結(jié)果為
t3:0
t2:0
t1:0
t2:1
t3:1
t1:1
t1:2
t2:2
t3:2
Task.Run通過(guò)輸入一個(gè)委托,創(chuàng)建一個(gè)任務(wù)并執(zhí)行,同時(shí)返回一個(gè)Task對(duì)象。
Task在執(zhí)行過(guò)程中,并不會(huì)等待命令行的主線程,所以在最后啟動(dòng)的Task后跟上Wait,即等待該線程結(jié)束之后,主線程才結(jié)束,從而讓printN的輸出內(nèi)容得以在終端中顯示。
Task類
上面的Task.Run的案例也可以用Task來(lái)實(shí)現(xiàn),但Task對(duì)象創(chuàng)建后,并不會(huì)馬上運(yùn)行,而會(huì)在Start()之后運(yùn)行,示例如下
void printN(object name) { for (int i = 0; i < 3; i++) { Console.WriteLine($"{name}:{i}"); Task.Delay(1000).Wait(); } } Action<object> act = (object name) => printN(name); Task t1 = new Task(()=>printN("t1")); new Task(act, "t2").Start(); Task t3 = new Task(act, "t3"); t1.Start(); t3.Start(); t3.Wait();
返回值
除了Task,C#還提供了帶有返回值的封裝,即Task<TResult>,可通過(guò)泛型的方式聲明返回值。
但是,Task說(shuō)到底還是個(gè)類,而非函數(shù),這個(gè)返回值并不會(huì)在構(gòu)造函數(shù)中體現(xiàn)出來(lái),也不經(jīng)過(guò)Start()函數(shù),若想使用這個(gè)返回值,需要經(jīng)由ContinueWith函數(shù)。
ContinueWith的功能是在某一個(gè)線程執(zhí)行完畢之后,開啟下一個(gè)線程。如果執(zhí)行完畢的線程有返回值的話,那么ContinueWith也可以利用這個(gè)返回值。
其調(diào)用方法為
Task<int> task = new Task<int>(() => { Console.WriteLine("這里是task"); return 100; }); //任務(wù)完成時(shí)執(zhí)行處理。 Task cwt = task.ContinueWith(t => { Console.WriteLine($"這里是Continue,task返回值為{t.Result}"); }); task.Start(); cwt.Wait();
其中,cwt需要等待task執(zhí)行完畢之后再執(zhí)行。
等待和延續(xù)
在前面的案例中,已經(jīng)講解了基本的等待函數(shù)Wait和基本的延續(xù)函數(shù)ContinueWith。C#中提供了更多的等待與延續(xù)函數(shù),以更加靈活地操作線程列表。
阻塞主線程 | 不阻塞主線程 | |
---|---|---|
任意線程執(zhí)行完畢即可執(zhí)行 | WaitAny | WhenAny |
所有線程執(zhí)行完畢方可執(zhí)行 | WaitAll | WhenAll |
其中, WhenAny, WhenAll需要與ContinueWith配合食用,當(dāng)WhenXX結(jié)束之后,即執(zhí)行ContinueWith中的內(nèi)容,這個(gè)過(guò)程并不阻塞主線程。
為了驗(yàn)證這些函數(shù)的功能,先創(chuàng)建一個(gè)線程列表
Action<string, int> log = (name, time) => { Console.WriteLine($"{name} Start..."); Task.Delay(time).Wait(); Console.WriteLine($"{name} Stop!"); }; Task[] tasks = new Task[] { Task.Run(() => log("A",3000)), Task.Run(() => log("B",1000)), Task.Run(() => log("C",2000)) };
然后依次執(zhí)行這幾個(gè)等待函數(shù),看看結(jié)果
Task.WaitAny(tasks); 此時(shí)當(dāng)B執(zhí)行完畢之后,阻塞就結(jié)束了,從而主線程結(jié)束。
B Start...
A Start...
C Start...
B Stop!
Task.WaitAll(tasks); 這次當(dāng)所有線程執(zhí)行完畢之后,程序才結(jié)束。
A Start...
B Start...
C Start...
B Stop!
C Stop!
A Stop!
下面這兩組測(cè)試,現(xiàn)象和前兩組相似,區(qū)別無(wú)非是在后面加上一段字符串而已。
Task.WhenAny(tasks).ContinueWith(x => Console.WriteLine($"某個(gè)Task執(zhí)行完畢")).Wait(); Task.WhenAll(tasks.ToArray()).ContinueWith(x => Console.WriteLine("所有Task執(zhí)行完畢")).Wait();
取消任務(wù)
C#提供了CancellationToken作為Task取消的標(biāo)識(shí),通過(guò)調(diào)用Cancel()函數(shù),可將其取消標(biāo)志更改為True,從而在線程執(zhí)行過(guò)程中,起到取消線程的作用。
首先創(chuàng)建一個(gè)可以取消的線程函數(shù)
int TaskMethod(string name, int seconds, CancellationToken token) { Console.WriteLine($"{name} 正在運(yùn)行"); for (int i = 0; i < seconds; i++) { Task.Delay(1000).Wait(); Console.WriteLine($"{name}: {i}s"); if (token.IsCancellationRequested) return -1; } return 1; }
功能很簡(jiǎn)單,就是跑循環(huán),在跑循環(huán)的過(guò)程中,如果token指明取消,則線程結(jié)束。
下面測(cè)試一下
var cts = new CancellationTokenSource(); var task = new Task<int>(() => TaskMethod("Task 1", 5, cts.Token), cts.Token); Console.WriteLine($"線程狀態(tài):{task.Status}"); task.Start(); Console.WriteLine($"線程狀態(tài):{task.Status}"); Task.Delay(3000).Wait(); cts.Cancel(); Console.WriteLine($"線程狀態(tài):{task.Status}"); Task.Delay(1000).Wait(); Console.WriteLine($"線程狀態(tài):{task.Status}");
效果為如下
線程狀態(tài):Created
線程狀態(tài):WaitingToRun
Task 1 正在運(yùn)行
Task 1: 0s
Task 1: 1s
線程狀態(tài):Running
Task 1: 2s
線程狀態(tài):RanToCompletion
在整個(gè)線程執(zhí)行的過(guò)程中,共出現(xiàn)了四種狀態(tài)
- Created 此時(shí)線程剛創(chuàng)建,但并未執(zhí)行
- WaitingToRun 此時(shí)已經(jīng)執(zhí)行了Start函數(shù),但線程還沒(méi)反應(yīng)過(guò)來(lái),所以是等待執(zhí)行
- Running 此時(shí)已經(jīng)執(zhí)行了Cancel,但task中的循環(huán)每1秒檢測(cè)1次,在Cancel執(zhí)行完之后,還沒(méi)來(lái)得及檢測(cè),就查詢了線程的狀態(tài),所以線程仍在運(yùn)行
- RanToCompletion 在等待1秒之后,終于檢測(cè)到token變了,從而線程結(jié)束。
到此這篇關(guān)于C#并發(fā)編程之Task類詳解的文章就介紹到這了,更多相關(guān)C#并發(fā)編程 Task類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用快捷鍵在Unity中快速鎖定和解鎖Inspector右上角的鎖功能
這篇文章主要為大家介紹了使用快捷鍵在Unity中快速鎖定和解鎖Inspector右上角的鎖功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08關(guān)于C#繼承的簡(jiǎn)單應(yīng)用代碼分析
在本篇文章里小編給大家整理了一篇關(guān)于C#繼承的簡(jiǎn)單應(yīng)用代碼分析內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-05-05Unity實(shí)現(xiàn)移動(dòng)端手勢(shì)解鎖功能
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)移動(dòng)端手勢(shì)解鎖功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07C#基于TCP協(xié)議的服務(wù)器端和客戶端通信編程的基礎(chǔ)教程
這篇文章主要介紹了C#基于TCP協(xié)議的服務(wù)器端和客戶端通信編程的基礎(chǔ)教程,文中講解了C#中TCP編程主要相關(guān)的TcpListener類與TcpClient類用法,需要的朋友可以參考下2016-04-04讓C# Excel導(dǎo)入導(dǎo)出 支持不同版本Office
讓C# Excel導(dǎo)入導(dǎo)出,支持不同版本的Office,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08C#實(shí)現(xiàn)網(wǎng)絡(luò)小程序的步驟詳解
經(jīng)常要檢測(cè)某些IP地址范圍段的計(jì)算機(jī)是否在線。有很多的方法,比如進(jìn)入到網(wǎng)關(guān)的交換機(jī)上去查詢、使用現(xiàn)成的工具或者編寫一個(gè)簡(jiǎn)單的DOS腳本等等,這些都比較容易實(shí)現(xiàn)。本文將用C#來(lái)實(shí)現(xiàn),感興趣的可以了解一下2022-12-12