2011-08-15 13 views
8

MSDN全体を見ていて、finallyブロック内でスリープしているときにスレッドを中断できない理由が見つかりません。私は成功しないで中止しようとしました。finallyブロックでスリープしているときにスレッドが中断されない理由

finallyブロック内でスリープ状態になるとスレッドを起こす方法はありますか?

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にはスレッドがfinallyブロックに中止することができます主張:http://msdn.microsoft.com/en-us/library/aa332364(v=vs.71).aspx は「finallyブロックが中止された場合には、finallyブロックが実行されている間のスレッドが中止できチャンスがあります。」スレッドは時々および/ finallyブロックで中断された中断されないことができ、なぜこれが説明しているので

編集 私は最高の答えとしてハンスアンパッサンのコメントを見つけます。そして、それはプロセスが閉鎖しているときです。 ありがとう

+3

、私は*一般的なルールとして、私は( '割り込みを避けることを知っています* ) 'をスレッド間のシグナリングのメカニズムとして使用します。 –

+0

Interrupt/Abortが最後に動作しなくなったとき、なぜMSDNがその逆を言っているのか、もっと詳しい説明やドキュメントを探していました。私の例は、より複雑なポーラータイプです。私は現在、それを提案した通りに読んでいます。 – Marek

+3

スレッドアボートには2種類あります。フレンドリーな人はfinallyブロックのコードを中断しません。不合理なものは、IsBackgroundがtrueと等しいスレッド、または処理されない例外で終了するスレッドでプロセスがシャットダウンするときに呼び出されます。今や、コードが不当に中断されても問題ありません。 –

答えて

8

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

のスレッドが互いに共同操作できるようにする代わりにシグナリングメカニズムを使用することを検討し、そう例えば、ブロックし、優雅にブロック解除ハンドル:

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()ははるかに少ないです。

あなたの質問に対する短い答えは、いいえ、AbortまたはInterruptを呼び出すことによってfinallyブロック内のスレッドを復帰させることはできません。

finallyブロックのスレッドを中止または中断できないのは設計通りです。最終的にブロックが期待どおりに実行できるように設計されています。 finallyブロックでスレッドを中止して中断することができれば、これはクリーンアップルーチンに予期しない結果をもたらす可能性があり、アプリケーションを破損した状態にしておくのは良いことです。

スレッドの割り込みにわずかなニュアンスがあるのは、スレッドがfinallyブロックに入る前のいつでも割り込みがスレッドに対して発行されている可能性がありますが、SleepWaitJoin状態ではない(つまりブロックされていない)ことです。この例では、finallyブロックにブロッキング呼び出しがあった場合、即座にThreadInterruptedExceptionがスローされ、finallyブロックからクラッシュします。最後にブロック保護がこれを防ぎます。

finallyブロックでの保護と同様に、これはtryブロックおよびCER(Constrained Execution Region)にも拡張されています。これは、領域が実行されるまでスローされる範囲をユーザコードで設定することができます。 でなければならず、遅延は中止されます。

と呼ばれるものは例外です。これらは、CLRホスティング環境自体によって提起された ThreadAbortExceptionsです。これらはfinallyブロックとcatchブロックを終了させますが、 ではなく、 CERです。たとえば、CLRは、作業の終了に時間がかかりすぎると判断したスレッドに応答して、失礼なアボートを発生させることができます。 AppDomainをアンロードしようとしたとき、またはSQL Server CLR内でコードを実行しようとしたとき。特定の例では、アプリケーションがシャットダウンしてAppDomainがアンロードされると、AppDomainのアンロードタイムアウトが発生するため、CLRはスリープ中のスレッドに対してRude Abortを発行します。

finallyブロックでの中止と中断は、ユーザーコードでは起こりませんが、2つの場合の動作が少し異なります。

中止

finallyブロック内のスレッドにAbortを呼び出し、呼び出し元のスレッドがブロックされています。これはdocumentedです:

中止されているスレッドが、そのようなcatchブロック、finallyブロック、または制約の実行領域として、コードの保護された領域内にある場合は中止がブロックされる可能性があります呼び出すスレッド。中止の場合

睡眠は無限ではなかった場合:

  1. それはここに停止し、すぐに進行せず、すなわち、呼び出し元のスレッドがAbortを出しますが、finallyブロックが終了するまでここにブロックします。 Joinステートメントに転送します。
  2. calleeスレッドの状態はAbortRequestedに設定されています。
  3. 被告人は眠っています。
  4. 呼び出し先がウェイクアップすると、状態がAbortRequestedであるため、finallyブロックコードの実行を継続して終了します。つまり、終了します。
  5. abortedスレッドがfinallyブロックを離れると、例外は発生せず、finallyブロックの後のコードは実行されず、スレッドの状態はAbortedになります。
  6. 呼び出しスレッドはブロック解除され、Joinステートメントに進み、呼び出されたスレッドが終了するとすぐに渡されます。睡眠は無限ではなかった場合

だから無限の睡眠であなたの例を考えると、呼び出し元のスレッドは、割り込みの場合には、ステップ1

割り込み

で永遠にブロックします:

あまり説明されていません...

  1. 呼び出し元のスレッドがInterruptを発行し、実行を継続していきます。
  2. 呼び出しスレッドはJoinステートメントでブロックされます。
  3. 呼び出し先スレッドは、次のブロッキング呼び出しで例外を発生するように設定されていますが、最終的にはブロックされているので、ブロックされていません。
  4. 被告人は眠っています。
  5. 呼び出し先が起動すると、finallyブロックの実行を継続します。
  6. 中断されたスレッドがfinallyブロックを離れると、次のブロック呼び出しでThreadInterruptedExceptionがスローされます(下記のコード例を参照)。
  7. 呼び出し元のスレッドが「ジョイン」と呼ばれ、スレッドが終了したように継続ステップ6で未処理のThreadInterruptedExceptionは今のプロセスを平坦化している、しかし、...

だからもう一度、無限の睡眠であなたの例を与えます呼び出し元のスレッドが永遠にブロックしますが、手順2

概要

でそうAbortInterruptが若干異なる振る舞いを持っているが、彼らは永遠に眠っていると呼ばれるスレッド結果、永遠のブロッキング呼び出し元のスレッド(の両方になりますあなたの例)。

ブロックされたスレッドがfinallyブロックを終了するように強制することができますが、これはCLRだけで発生する可能性があります。ThreadAbortException.ExceptionStateを反映させることもできません。内部CLR呼び出しによってAbortReason - 簡単に悪になる機会はありません...)。

CLRは、ユーザーコードが最終的にブロックを早期に終了させないようにします。破損した状態を防ぐのに役立ちます。 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 
    } 
} 
+0

+1素晴らしい答え。 –

4

ブロックは、割り込みや中断の影響を受けないものを保持し、何らかの問題があっても正常に実行されます。 finallyブロックを中断または中断させることは、その点をかなり凌駕します。悲しいことに、あなたが指摘したように、finallyブロックは、さまざまな競合状態のため中止または中断することができます。このため、スレッドを中断したり中止したりしないように多くの人にアドバイスします。

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

関連する問題