C# - System.Threading - スレッド停止

 クラウディア
1. 概要
2. Thread.Interrupt
3. Task CancellationTokenSource

1. 概要

 スレッドは、基本的には、終了するまで待ちますが。  起動もとで、起動したスレッドを終了させたい事象があります。  本ページは、下記のサイトを参考にさせていただきました。
Thread.Interrupt メソッド (System.Threading) 」
「CancellationTokenSource クラス (System.Threading)」
「【C#】Task をキャンセルする

2. Thread.Interrupt

 「Thread」の停止には、「Interrupt()」メソッドを使用します。  下記のソースを書いてみました(メインの呼び出し等は略、8行目の run() メソッドから動作します)。

public class myClass
{
	private System.Timers.Timer? timer;
	private Thread? thread;

	public myClass() {}

	public void run()
	{
		Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method start");
		startTimer();

		thread = new Thread(new ThreadStart(myThread));
		thread.Start();
		thread.Join();
	}

	private void startTimer()
	{
		if (timer != null)
		{
			if (timer.Enabled)
			{
				return;
			}
		}

		timer = new System.Timers.Timer(3 * 1000);
		timer.Elapsed += stop;
		timer.AutoReset = false;
		timer.Start();
	}

	private void stop(object? sender, EventArgs e)
	{
		Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method start");

		if (timer != null)
		{
			thread.Interrupt();
			timer.Dispose();
			timer = null;
		}
	}

	private void myThread()
	{
		Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method start");

		try
		{
			for(int i=0; i<5; i++)
			{
				Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method " + i);
				Thread.Sleep(1000);
			}
		}
		catch (ThreadInterruptedException)
		{
			Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " Interrupted");
		}

		Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method end");
	}
}
 起動して、3秒後に、40行目で「Interrupt()」をコールしています。  その結果、スレッド側に、「ThreadInterruptedException」が発生して、それをキャッチして終了しています。  実行すると、下記の結果が得られます。

run          method start
myThread     method start
myThread     method 0
myThread     method 1
myThread     method 2
stop         method start
myThread     Interrupted
myThread     method end

3. Task CancellationTokenSource

 「Task」の停止には、「CancellationTokenSource」というクラスを、起動時に紐づけしておいて、停止時に、「CancellationTokenSource.Cancel()」メソッドをコールすることで、停止を要求します。  下記のソースを書いてみました(メインの呼び出し等は略、9行目の run() メソッドから動作します)。

public class myClass
{
	private Task? taskSon;
	private System.Timers.Timer? timer;
	private static CancellationTokenSource cts = new CancellationTokenSource();

	public myClass() {}

	public void run()
	{
		Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method start");
		startTimer();

		taskSon = Task.Run(() => { TaskSon(); }, cts.Token);
		taskSon.Wait();
	}

	private void startTimer()
	{
		if (timer != null)
		{
			if (timer.Enabled)
			{
				return;
			}
		}

		timer = new System.Timers.Timer(3 * 1000);
		timer.Elapsed += stop;

		// 1回だけ実行
		timer.AutoReset = false;
		timer.Start();
	}

	private void stop(object? sender, EventArgs e)
	{
		Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method start");

		if (timer != null)
		{
			if (cts.Token.CanBeCanceled)
			{
				Console.WriteLine("call cts.Cancel()");
				cts.Cancel();
			}

			timer.Dispose();
			timer = null;
		}
	}

	private void TaskSon()
	{
		Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method start");

		for(int i=0; i<10; i++)
		{
			Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " method " + i.ToString());
			Thread.Sleep(1000);

			if (cts.IsCancellationRequested)
			{
				Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name.PadRight(12) + " canceled");
				break;
			}
		}

		cts.Dispose();
	}
}
 起動して、3秒後に、44行目で「CancellationTokenSource.Cancel()」メソッドをコールしています。  スレッド側で、定期的に、「CancellationTokenSource.IsCancellationRequested」プロパティをチェックしていて。  「true」になった時点で処理を中止して、終了します。  当初、「CancellationTokenSource.IsCancellationRequested」プロパティのチェックがわからなくて、勝手に止まるものと思い込んでいましたが、止まらないので焦りました。  実行すると、下記の結果が得られます。

run          method start
TaskSon      method start
TaskSon      method 0
TaskSon      method 1
TaskSon      method 2
stop         method start
call cts.Cancel()
TaskSon      canceled
AbemaTV 無料体験
葬送のフリーレン Prime Video