執行緒的中斷範例實作,以及前景背景小知識

一直沒有執行緒中斷的需求,直到最近有遇到這方面的問題,才開始翻這方面的知識,為了怕自己衰老記憶力不好,因此把它寫成文章記錄下來。下圖是KOF 99'引入SuperCancel的概念,我們後來的CancellationTokenSource工作物件我會以這個名稱來命名,用來紀念這個概念在該年代的創新。

在這裡我想要使用一個Console Main方法來完全演繹這方面的程式工作過程,這中間包含了下列幾項特點:

  1. 執行緒的手動中斷。
  2. 執行緒的自動(幾秒後)自我中斷。
  3. 執行緒的中斷後的事件委派(delegate)。
  4. 執行緒正常運行完成後的取值。
  5. 執行緒中斷運行完成後的取值。
  6. 判斷執行緒是否已經正常運行完成。
  7. 判斷執行緒是否已經被中斷完成。

程式碼如下:

static void Main(string[] args)
{
	Write("請輸入幾毫秒後執行緒自動中斷,本執行緒最多執行10秒(20 * 500):");
	int iCancelTime = System.Convert.ToInt32(ReadLine());
	Write("--- 請輸入任意鍵中斷執行緒 ----");
	//宣告可讓執行緒中斷之物件
	System.Threading.CancellationTokenSource oSuperCancel = new System.Threading.CancellationTokenSource(iCancelTime);
	//委派一個方法,讓執行緒中斷後回到此處運行
	oSuperCancel.Token.Register(() => { WriteLine("\n「主程序」被「執行緒」告知他自己被中斷了。"); });
	//生出一個TOKEN物件傳入執行緒內,以便執行緒讀取狀態,判斷是否應該進行中斷工作
	System.Threading.CancellationToken oToken = oSuperCancel.Token;
	//宣告一個執行緒起來,並特別留下一個oTask以供給之後判斷是否執行完成用
	var oTask = System.Threading.Tasks.Task.Run<string>(() => {
		WriteLine($"\n編號【{System.Threading.Thread.CurrentThread.ManagedThreadId}】執行緒已經開始運行。");
		for (int i = 0; i < 20; i++)
		{
			if (oToken.IsCancellationRequested)
			{
				return "「執行緒」自己發現被「主程序」中斷了。";
			}
			Write(".");
			System.Threading.Thread.Sleep(500);
		}
		return "執行緒自己跑完了。";
	}, oToken).ContinueWith(oFinishTask => {
		WriteLine($"\n執行緒結果:{oFinishTask.Result}");
		return;
	});
	//讀取按鈕(主程序到這一行會被中斷卡住,藉以等候執行緒)
	ReadKey();
	//如果執行緒還沒結束,而且執行緒還沒有被中斷過,就進行中斷
	if (!oTask.IsCompleted && !oToken.IsCancellationRequested)
	{
		oSuperCancel.Cancel();
		ReadKey();
	}
}

基本上,所有的註解已經說明了程式碼中大部分的工作,我在這邊略提幾點說明。

  1. 執行緒手動中斷後,delegate在Token.Register()裡面的優先權,會大於執行緒回傳優先權。也就是說程式碼會先印出「主程序被執行緒告知他自己被中斷了」後再印出「執行緒自己發現被主程序中斷了」。
  2. 執行緒的生成,這邊採用的是此方法Task.Run<TResult> Method (Func<TResult>, CancellationToken)
  3. 作為CallBack函式的ContinueWith,在這邊是採用此方法Task.ContinueWith Method (Action<Task>)。oFinishTasky在此作為Action<Task>的引數,亦即資料型別是System.Threading.Tasks.Task<string>。
  4. 程式碼中用IsCompleted來檢查執行緒是否已經執行結束。
  5. 程式碼中用CancellationTokenSource.Token.IsCancellationRequested來檢查執行緒是否已經被中斷。

執行緒的前景、背景、線程池小知識

前景、背景執行緒

  1. 前景執行緒:不會隨著Process關閉而關閉,一定會執行完工作。(這是.NET的預設值)
  2. 背景執行緒:隨著Process關閉而關閉,不會執行完工作且不會有Exception。
  3. 前景與背景執行緒,可以透過IsBackground屬性來切換。

線程池(ThreadPool)

ThreadCancel TaskCancel CancellationTokenSource CancellationToken