在剛接觸後臺線程的時候,覺得線程神秘且高深,並且時常有先輩們千叮萬囑:能不用的時候,儘量不要用,千萬不要濫用線程,否則會發生預料不到的結果。在接觸線程一段時間後,感覺線程也不過如此,輕而易舉的就可以創建,所以逐漸大膽起來,項目里隨處可見的都是Task,Thread,async,await等內容。在大... ...
在剛接觸後臺線程的時候,覺得線程神秘且高深,並且時常有先輩們千叮萬囑:能不用的時候,儘量不要用,千萬不要濫用線程,否則會發生預料不到的結果。在接觸線程一段時間後,感覺線程也不過如此,輕而易舉的就可以創建,所以逐漸大膽起來,項目里隨處可見的都是Task,Thread,async,await等內容。在大多情況下,我們只關心線程的創建與啟動,運行,卻並不關心線程的結束或者終止。今天這篇文章,我們就以一些簡單的小例子,簡述如何有效的停止線程,僅供學習分享使用,如有不足之處,還請指正。
需求說明
現在有一個需求:有一個後臺線程,定時(300ms)輸出一段內容,但不希望它一直運行,所以設置了超時時間(3s),希望在超時結束後,便執行後續的內容。
初始版本
根據需求,開發了第一個版本的代碼,步驟如下:
- 定義一個Task。
- 在Task內,運行死迴圈,每間隔300毫秒,輸出一段內容。
- 設置Task的等待超時時間,超時結束後,運行後續內容。
具體代碼如下所示:
1 namespace DemoTask 2 { 3 internal class Program 4 { 5 static void Main(string[] args) 6 { 7 TestTask(); 8 Console.ReadKey(); 9 } 10 11 /// <summary> 12 /// 測試任務 13 /// </summary> 14 public static void TestTask() 15 { 16 Console.WriteLine("程式開始."); 17 var task = Task.Run(() => 18 { 19 while (true) 20 { 21 Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}程式正在運行..."); 22 Thread.Sleep(300); 23 } 24 }); 25 task.Wait(3000); 26 Console.WriteLine("程式超時結束."); 27 } 28 } 29 }
信心滿滿的運行程式,但是期待的結果並沒有出現,在超時時間後,並沒有預期的停止任務,反而在繼續運行。如下所示:
註意:通過以上程式發現,Wait方法只是等待時間結束後不再等待,但是原有任務並未結束,而是繼續運行。
進階版本
為瞭解決線程無法結束的問題,微軟官方給出的方案是採用CancellationTokenSource,嚮應該被取消的線程發送信號。即線上程內部判斷是否收到取消請求,在外部發起取消請求信號。步驟如下:
- 定義一個Task。
- 在Task內,當沒有收到取消信號時,每間隔300毫秒,輸出一段內容。
- 設置Task的等待超時時間,超時結束後,發起取消信號,並運行後續內容。
具體代碼如下所示:
1 /// <summary> 2 /// 測試任務 3 /// </summary> 4 public static void TestTask() 5 { 6 Console.WriteLine("程式開始."); 7 CancellationTokenSource cts = new CancellationTokenSource(); 8 CancellationToken token = cts.Token; 9 var task = Task.Run(() => 10 { 11 while (!token.IsCancellationRequested) 12 { 13 Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}程式正在運行..."); 14 Thread.Sleep(300); 15 } 16 }); 17 bool flag = task.Wait(3000); 18 if (!flag) { 19 cts.Cancel(); 20 } 21 Console.WriteLine("程式超時結束."); 22 }
優化程式後,運行程式如下所示:
註意:經過以上程式優化後,確實是如預想的結果一致,程式在等待超時時間後,停止了運行。
最終版本
正常情況下,如果是我們自己開發的程式,程式到第二個版本就已經解決問題了,但是假如While迴圈的內容是第三方提供的程式,已經封裝為固定模塊,我們無法進行修改,那應該如何才能終止死迴圈呢?如何才能像任務管理器結束進程一樣,結束這一直無休止運行的程式呢?
為瞭解決我們的難題,對程式進行進一步的優化,步驟如下:
- 定義一個Task。
- 在Task內,註冊線程的Abort方法,在未調用Abort方法時,每間隔300毫秒,輸出一段內容。
- 設置Task的等待超時時間,超時結束後,發起取消信號,並運行後續內容。
具體代碼如下所示:
1 /// <summary> 2 /// 測試任務 3 /// </summary> 4 public static void TestTask() 5 { 6 Console.WriteLine("程式開始."); 7 CancellationTokenSource cts = new CancellationTokenSource(); 8 CancellationToken token = cts.Token; 9 var task = Task.Run(() => 10 { 11 using (token.Register((Thread.CurrentThread.Abort))) 12 { 13 //假設以下內容第3方提供,無法修改 14 while (true) 15 { 16 Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}程式正在運行..."); 17 Thread.Sleep(300); 18 } 19 //以上內容第3方提供 20 } 21 }); 22 bool flag = task.Wait(3000); 23 if (!flag) 24 { 25 cts.Cancel(); 26 } 27 Console.WriteLine("程式超時結束."); 28 }
優化程式後,運行程式如下所示:
以上就是我們對後臺服務線程的終止問題的探索,解決問題的方式還有很多,希望本篇文章可以拋磚引玉,一起學習,共同進步。
作者:小六公子
出處:http://www.cnblogs.com/hsiang/
本文版權歸作者和博客園共有,寫文不易,支持原創,歡迎轉載【點贊】,轉載請保留此段聲明,且在文章頁面明顯位置給出原文連接,謝謝。
關註個人公眾號,定時同步更新技術及職場文章