詳解C#如何優(yōu)雅地終止線程
在剛接觸后臺線程的時候,覺得線程神秘且高深,并且時常有先輩們千叮萬囑:能不用的時候,盡量不要用,千萬不要濫用線程,否則會發(fā)生預(yù)料不到的結(jié)果。在接觸線程一段時間后,感覺線程也不過如此,輕而易舉的就可以創(chuàng)建,所以逐漸大膽起來,項目里隨處可見的都是Task,Thread,async,await等內(nèi)容。在大多情況下,我們只關(guān)心線程的創(chuàng)建與啟動,運行,卻并不關(guān)心線程的結(jié)束或者終止。今天這篇文章,我們就以一些簡單的小例子,簡述如何有效的停止線程,僅供學習分享使用,如有不足之處,還請指正。
需求說明
現(xiàn)在有一個需求:有一個后臺線程,定時(300ms)輸出一段內(nèi)容,但不希望它一直運行,所以設(shè)置了超時時間(3s),希望在超時結(jié)束后,便執(zhí)行后續(xù)的內(nèi)容。
初始版本
根據(jù)需求,開發(fā)了第一個版本的代碼,步驟如下:
- 定義一個Task。
- 在Task內(nèi),運行死循環(huán),每間隔300毫秒,輸出一段內(nèi)容。
- 設(shè)置Task的等待超時時間,超時結(jié)束后,運行后續(xù)內(nèi)容。
具體代碼如下所示:
namespace DemoTask { internal class Program { static void Main(string[] args) { TestTask(); Console.ReadKey(); } /// <summary> /// 測試任務(wù) /// </summary> public static void TestTask() { Console.WriteLine("程序開始."); var task = Task.Run(() => { while (true) { Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}程序正在運行..."); Thread.Sleep(300); } }); task.Wait(3000); Console.WriteLine("程序超時結(jié)束."); } } }
信心滿滿的運行程序,但是期待的結(jié)果并沒有出現(xiàn),在超時時間后,并沒有預(yù)期的停止任務(wù),反而在繼續(xù)運行。如下所示:
注意:通過以上程序發(fā)現(xiàn),Wait方法只是等待時間結(jié)束后不再等待,但是原有任務(wù)并未結(jié)束,而是繼續(xù)運行。
進階版本
為了解決線程無法結(jié)束的問題,微軟官方給出的方案是采用CancellationTokenSource,向應(yīng)該被取消的線程發(fā)送信號。即在線程內(nèi)部判斷是否收到取消請求,在外部發(fā)起取消請求信號。步驟如下:
- 定義一個Task。
- 在Task內(nèi),當沒有收到取消信號時,每間隔300毫秒,輸出一段內(nèi)容。
- 設(shè)置Task的等待超時時間,超時結(jié)束后,發(fā)起取消信號,并運行后續(xù)內(nèi)容。
具體代碼如下所示:
/// <summary> /// 測試任務(wù) /// </summary> public static void TestTask() { Console.WriteLine("程序開始."); CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; var task = Task.Run(() => { while (!token.IsCancellationRequested) { Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}程序正在運行..."); Thread.Sleep(300); } }); bool flag = task.Wait(3000); if (!flag) { cts.Cancel(); } Console.WriteLine("程序超時結(jié)束."); }
優(yōu)化程序后,運行程序如下所示:
注意:經(jīng)過以上程序優(yōu)化后,確實是如預(yù)想的結(jié)果一致,程序在等待超時時間后,停止了運行。
最終版本
正常情況下,如果是我們自己開發(fā)的程序,程序到第二個版本就已經(jīng)解決問題了,但是假如While循環(huán)的內(nèi)容是第三方提供的程序,已經(jīng)封裝為固定模塊,我們無法進行修改,那應(yīng)該如何才能終止死循環(huán)呢?如何才能像任務(wù)管理器結(jié)束進程一樣,結(jié)束這一直無休止運行的程序呢?
為了解決我們的難題,對程序進行進一步的優(yōu)化,步驟如下:
- 定義一個Task。
- 在Task內(nèi),注冊線程的Abort方法,在未調(diào)用Abort方法時,每間隔300毫秒,輸出一段內(nèi)容。
- 設(shè)置Task的等待超時時間,超時結(jié)束后,發(fā)起取消信號,并運行后續(xù)內(nèi)容。
具體代碼如下所示:
/// <summary> /// 測試任務(wù) /// </summary> public static void TestTask() { Console.WriteLine("程序開始."); CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; var task = Task.Run(() => { using (token.Register((Thread.CurrentThread.Abort))) { //假設(shè)以下內(nèi)容第3方提供,無法修改 while (true) { Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}程序正在運行..."); Thread.Sleep(300); } //以上內(nèi)容第3方提供 } }); bool flag = task.Wait(3000); if (!flag) { cts.Cancel(); } Console.WriteLine("程序超時結(jié)束."); }
優(yōu)化程序后,運行程序如下所示:
到此這篇關(guān)于詳解C#如何優(yōu)雅地終止線程的文章就介紹到這了,更多相關(guān)C#終止線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#如何安全、高效地玩轉(zhuǎn)任何種類的內(nèi)存之Span的本質(zhì)
為什么要使用指針,什么時候需要使用它,以及如何安全、高效地使用它?本文將講清楚 What、How 和 Why ,讓你知其然,更知其所以然2021-08-08C#雙向鏈表LinkedList排序?qū)崿F(xiàn)方法
這篇文章主要介紹了C#雙向鏈表LinkedList排序?qū)崿F(xiàn)方法,涉及C#雙向鏈表的定義與排序技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08js substr,substring與java substring和C# substring的區(qū)別解析
本篇文章主要是對js中substr,substring與java中substring和C#中substring的區(qū)別進行了介紹,需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01C#創(chuàng)建安全的棧(Stack)存儲結(jié)構(gòu)
這篇文章主要為大家詳細介紹了C#創(chuàng)建安全的棧(Stack)存儲結(jié)構(gòu)的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10