こんにちは。
本日はThreadを使用した非同期処理についてのメモです。
概要
非同期処理を実現する際に使用可能なものはいくつかありますが
その中でも最も基本であり、レガシーと言われるThreadクラスを使用した
非同期処理についてのメモです。
スレッドの開始と待機(Start、Join)
以下のコードでは、
HeavyProcという時間のかかる処理を非同期で処理しています。
public void Proc() { var t = new Thread(HeavyProc); t.Start(); t.Join(); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][Proc] End!"); } public void HeavyProc() { for (var i = 0; i < 10; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][HeavyProc] i = {i}"); } }
実行結果は以下のようになります。
[15:13:25][HeavyProc] i = 0 [15:13:26][HeavyProc] i = 1 [15:13:27][HeavyProc] i = 2 [15:13:28][HeavyProc] i = 3 [15:13:29][HeavyProc] i = 4 [15:13:30][HeavyProc] i = 5 [15:13:31][HeavyProc] i = 6 [15:13:32][HeavyProc] i = 7 [15:13:33][HeavyProc] i = 8 [15:13:34][HeavyProc] i = 9 [15:13:34][Proc] End!
HeavyProcでは1秒間隔でループを10回実行しています。
Procでは、非同期でHeavyProcを実行して待機しています。
開始はt.Start();
、待機はt.Join();
です。
なので、HeavyProcが終了するのを待機し、
完了した後に処理が終了となっています。
バックグラウンドとフォアグラウンド
new Thread
を用いて作成したスレッドは
デフォルトでフォアグラウンドスレッドとなります。
フォアグラウンドスレッドの場合、呼び出し元のスレッドが終了しても
終了せずに処理が終わるまで動作します。
バックグラウンドスレッドの場合、呼び出し元のスレッドが終了すると
処理が終わるのを待機せずに終了します。
先程のサンプルコードを使用して
バックグラウンドを指定すると以下のようになります。
var t = new Thread(HeavyProc) { IsBackground = true };
スレッドに引数を渡す
以下のサンプルでは、HeavyProcにループ回数を
引数で渡すようにしています。
引数はStart()
の引数に与えます。
public void Proc() { var t = new Thread(HeavyProc) { IsBackground = true }; t.Start(6); t.Join(); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][Proc] End!"); } public void HeavyProc(object obj) { int max; try { max = (int) obj; } catch (Exception) { max = 10; } Console.WriteLine($"[HeavyProc] max = {max}"); for (var i = 0; i < max; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][HeavyProc] i = {i}"); } }
実行結果は以下のようになります。
[HeavyProc] max = 6 [15:14:38][HeavyProc] i = 0 [15:14:39][HeavyProc] i = 1 [15:14:40][HeavyProc] i = 2 [15:14:41][HeavyProc] i = 3 [15:14:42][HeavyProc] i = 4 [15:14:43][HeavyProc] i = 5 [15:14:43][Proc] End!
スレッドの中断(Abort → 非推奨)
Abortを使用するとスレッドの中断が可能になります。
が、Abortは非推奨となっています。
.Net5以降では以下のようにコンパイル時に警告が発生します。
warning SYSLIB0006: 'Thread.Abort()' は旧形式です ('Thread.Abort is not supported and throws PlatformNotSupportedException.')
.Net5より前のバージョンでは、
実行時に例外「PlatformNotSupportedException」が発生します。
スレッドの中断(Interrupt)
以下のサンプルでは、Interruptを使用してスレッドを中断しています。
Interruptを呼び出すと、スレッド側でThreadInterruptedException
が発生します。
これを検知することで、スレッドの中断が可能となります。
public void Proc() { var t = new Thread(HeavyProc) { IsBackground = true }; t.Start(); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][Proc] Thread Start"); Thread.Sleep(TimeSpan.FromSeconds(1)); t.Interrupt(); Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][Proc] End!"); } public void HeavyProc() { try { Thread.Sleep(TimeSpan.FromSeconds(10)); } catch (ThreadInterruptedException) { Console.WriteLine($"[{DateTime.Now:HH:mm:ss}][HeavyProc] ThreadInterruptedException発生"); } }
実行結果は以下のようになります。
HeavyProcは10秒待機する処理ですが、ProcからInterruptが呼び出されることによって
ThreadInterruptedExceptionが発生し、処理が終了しています。
[15:24:08][Proc] Thread Start [15:24:09][HeavyProc] ThreadInterruptedException発生 [15:24:10][Proc] End!
以上です。