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
|
|