9

私はMSDN全体を見てきましたが、最終ブロック内でスリープしているときにスレッドを中断できない理由を見つけることができません。中止を試みましたが成功しませんでした。

最終ブロック内でスリープしているときにスレッドを起こす方法はありますか?

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
    {
        try
        {
            Thread.Sleep(Timeout.Infinite);
        }
        catch (ThreadInterruptedException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

驚くべきことに、MSDN は、最終ブロックでスレッド を中止できると主張しています: http://msdn.microsoft.com/en-us/library/aa332364(v=vs.71).aspxこの場合、finally ブロックは中止されます。」

編集 Hans Passant のコメントが最良の回答であることがわかりました。そして、それはプロセスがシャットダウンしているときです。ありがとう

4

2 に答える 2

9

実行中のプログラムの状態が損なわれる可能性があるため、スレッドの中止と中断はできるだけ避ける必要があります。たとえば、リソースに対して開いているロックを保持していたスレッドを中止したとします。これらのロックは決して解放されません。

代わりに、スレッドが互いに協力してブロックとブロック解除を適切に処理できるように、シグナリングメカニズムの使用を検討してください。次に例を示します。

    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 を呼び出すスレッドはブロックされる可能性があります。

スリープが無限ではなかった場合の中止の場合:

  1. 呼び出しスレッドは、finally ブロックが終了するまで、ここで but ブロックを発行します。つまり、ここで停止し、ステートメント Abortにすぐには進みません。Join
  2. 呼び出し先スレッドの状態は に設定されていAbortRequestedます。
  3. 呼び出し先は眠り続けます。
  4. 呼び出し先がウェイクアップすると、その状態があるAbortRequestedため、finally ブロック コードを実行し続け、「蒸発」、つまり終了します。
  5. 中止されたスレッドが finally ブロックを離れるとき: 例外は発生せず、finally ブロックの後のコードは実行されず、スレッドの状態はAbortedです。
  6. 呼び出しスレッドはブロックされず、Joinステートメントに進み、呼び出されたスレッドが終了するとすぐに渡されます。

したがって、無限スリープの例を考えると、呼び出し元のスレッドはステップ 1 で永久にブロックされます。

割り込み

スリープが無限でない場合の割り込みの場合:

十分に文書化されていません...

  1. 呼び出しスレッドは を発行し、実行Interrupt継続します。
  2. 呼び出し元のスレッドはJoinステートメントでブロックされます。
  3. 呼び出し先スレッドは、次のブロック呼び出しで例外を発生させるように設定された状態を持っていますが、決定的に、finally ブロックにあるため、ブロックが解除されません。つまり、起こされません。
  4. 呼び出し先は眠り続けます。
  5. 呼び出し先が起動すると、finally ブロックの実行が続行されます。
  6. 中断されたスレッドが finally ブロックを離れるとThreadInterruptedException、次のブロッキング呼び出しで がスローされます (以下のコード例を参照)。
  7. 呼び出しスレッドは「参加」し、呼び出されたスレッドが終了すると続行しますが、ThreadInterruptedExceptionステップ 6 で未処理のプロセスがフラット化されました...

繰り返しますが、無限スリープの例を考えると、呼び出し元のスレッドは永久にブロックされますが、ステップ 2 で.

概要

したがって、AbortInterruptの動作はわずかに異なりますが、どちらも呼び出されたスレッドが永久にスリープ状態になり、呼び出し元のスレッドが永久にブロックされます (この例では)。

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
    }
}   
于 2011-08-15T12:44:58.157 に答える
4

ブロックの要点はfinally、割り込みやアボートの影響を受けず、何があっても通常の完了まで実行される何かを保持することです。finallyブロックが中止または中断されることを許可すると、ポイントがほとんど無効になります。悲しいことに、ご指摘のとおり、finallyさまざまな競合状態が原因で、ブロックが中止または中断される可能性があります。これが、スレッドを中断したり中止したりしないようにアドバイスする多くの人を目にする理由です。

代わりに、協調設計を使用してください。スレッドが中断されることになっている場合は、 を呼び出す代わりにSleep、時限待機を使用します。signalを呼び出す代わりにInterrupt、スレッドが待機するものを呼び出します。

于 2011-08-15T12:33:52.560 に答える