実行中のプログラムの状態が損なわれる可能性があるため、スレッドの中止と中断はできるだけ避ける必要があります。たとえば、リソースに対して開いているロックを保持していたスレッドを中止したとします。これらのロックは決して解放されません。
代わりに、スレッドが互いに協力してブロックとブロック解除を適切に処理できるように、シグナリングメカニズムの使用を検討してください。次に例を示します。
private readonly AutoResetEvent ProcessEvent = new AutoResetEvent(false);
private readonly AutoResetEvent WakeEvent = new AutoResetEvent(false);
public void Do()
{
Thread th1 = new Thread(ProcessSomething);
th1.IsBackground = false;
th1.Start();
ProcessEvent.WaitOne();
Console.WriteLine("Processing started...");
Thread th2 = new Thread(() => WakeEvent.Set());
th2.Start();
th1.Join();
Console.WriteLine("Joined");
}
private void ProcessSomething()
{
try
{
Console.WriteLine("Processing...");
ProcessEvent.Set();
}
finally
{
WakeEvent.WaitOne();
Console.WriteLine("Woken up...");
}
}
アップデート
非常に興味深い低レベルの問題です。Abort()
文書化されていますが、そうでInterrupt()
はありません。
あなたの質問に対する短い答えはノーです.finallyブロックでスレッドを呼び出すことによって、Abort
またはInterrupt
それを呼び出すことはできません。
finally ブロックでスレッドを中止または中断できないのは設計によるものであり、単に finally ブロックが期待どおりに実行される可能性があるためです。finally ブロックでスレッドを中止して中断できると、クリーンアップ ルーチンに意図しない結果が生じる可能性があり、アプリケーションが破損した状態のままになる可能性があります。これは良くありません。
スレッド割り込みのわずかなニュアンスは、スレッドが finally ブロックに入る前の任意の時点でスレッドに対して割り込みが発行された可能性があることSleepWaitJoin
です。この例では、finally ブロックにブロッキング呼び出しがあった場合、すぐに がスローされ、finally ブロックThreadInterruptedException
からクラッシュします。最後に、ブロック保護がこれを防ぎます。
これは、finally ブロックでの保護と同様に、try ブロックや、領域が実行されるまで一連の例外がスローされるのを防ぐためにユーザー コードで構成できるCER ( Constrained Execution Region ) にも拡張されます。コードの重要なブロックに非常に役立ちます。これは完了し、アボートを遅らせる必要があります。
これに対する例外 (しゃれは意図されていません) は、 Rude Abortsと呼ばれるものです。これらはThreadAbortExceptions
、CLR ホスティング環境自体によって発生します。これらにより、finally ブロックと catch ブロックが終了する可能性がありますが、CERは発生しません。たとえば、CLR は、AppDomain をアンロードしようとしたり、SQL Server CLR 内でコードを実行しようとした場合など、処理や終了に時間がかかりすぎると判断されたスレッドに応答して、Rude Aborts を発生させる場合があります。特定の例では、アプリケーションがシャットダウンして AppDomain がアンロードされると、AppDomain のアンロード タイムアウトが発生するため、CLR はスリープ状態のスレッドで Rude Abort を発行します。
最終ブロックでの中止と中断はユーザー コードでは発生しませんが、2 つのケースでは動作が少し異なります。
アボート
finally ブロックでスレッドを呼び出すAbort
と、呼び出し元のスレッドがブロックされます。これは文書化されています:
中止されるスレッドがコードの保護された領域 (catch ブロック、finally ブロック、または制約付き実行領域など) にある場合、Abort を呼び出すスレッドはブロックされる可能性があります。
スリープが無限ではなかった場合の中止の場合:
- 呼び出しスレッドは、finally ブロックが終了するまで、ここで but ブロックを発行します。つまり、ここで停止し、ステートメント
Abort
にすぐには進みません。Join
- 呼び出し先スレッドの状態は に設定されてい
AbortRequested
ます。
- 呼び出し先は眠り続けます。
- 呼び出し先がウェイクアップすると、その状態がある
AbortRequested
ため、finally ブロック コードを実行し続け、「蒸発」、つまり終了します。
- 中止されたスレッドが finally ブロックを離れるとき: 例外は発生せず、finally ブロックの後のコードは実行されず、スレッドの状態は
Aborted
です。
- 呼び出しスレッドはブロックされず、
Join
ステートメントに進み、呼び出されたスレッドが終了するとすぐに渡されます。
したがって、無限スリープの例を考えると、呼び出し元のスレッドはステップ 1 で永久にブロックされます。
割り込み
スリープが無限でない場合の割り込みの場合:
十分に文書化されていません...
- 呼び出しスレッドは を発行し、実行
Interrupt
を継続します。
- 呼び出し元のスレッドは
Join
ステートメントでブロックされます。
- 呼び出し先スレッドは、次のブロック呼び出しで例外を発生させるように設定された状態を持っていますが、決定的に、finally ブロックにあるため、ブロックが解除されません。つまり、起こされません。
- 呼び出し先は眠り続けます。
- 呼び出し先が起動すると、finally ブロックの実行が続行されます。
- 中断されたスレッドが finally ブロックを離れると
ThreadInterruptedException
、次のブロッキング呼び出しで がスローされます (以下のコード例を参照)。
- 呼び出しスレッドは「参加」し、呼び出されたスレッドが終了すると続行しますが、
ThreadInterruptedException
ステップ 6 で未処理のプロセスがフラット化されました...
繰り返しますが、無限スリープの例を考えると、呼び出し元のスレッドは永久にブロックされますが、ステップ 2 で.
概要
したがって、Abort
とInterrupt
の動作はわずかに異なりますが、どちらも呼び出されたスレッドが永久にスリープ状態になり、呼び出し元のスレッドが永久にブロックされます (この例では)。
Rude Abort のみが、ブロックされたスレッドに finally ブロックを強制的に終了させることができ、これらは CLR 自体によってのみ発生させることができます (リフレクションを使用してThreadAbortException.ExceptionState
内部 CLR 呼び出しを行って取得することはできませAbortReason
ん。簡単に悪になる機会はありません) 。そこの...)。
CLR は、ユーザー コードによって finally ブロックが早期に終了されるのを防ぎます。これは、破損した状態を防ぐのに役立ちます。
とのわずかに異なる動作の例Interrupt
:
internal class ThreadInterruptFinally
{
public static void Do()
{
Thread t = new Thread(ProcessSomething) { IsBackground = false };
t.Start();
Thread.Sleep(500);
t.Interrupt();
t.Join();
}
private static void ProcessSomething()
{
try
{
Console.WriteLine("processing");
}
finally
{
Thread.Sleep(2 * 1000);
}
Console.WriteLine("Exited finally...");
Thread.Sleep(0); //<-- ThreadInterruptedException
}
}