Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

スレッドプール側で投げる例外に注意

http://d.hatena.ne.jp/akiramei/20060124/p1
↑は、ちょっと説明が足りなすぎなので補足します。

using System;
using System.Threading;

delegate void SilverSkin ();

class Program {
    static void Lance () {
        throw new Exception ("届け…");
    }

    static void Callback (IAsyncResult r) {
        SilverSkin ss = r.AsyncState as SilverSkin;
        Console.WriteLine ("In Callback.");
        try {
            ss.EndInvoke (r);
        }
        catch (Exception) {
            Console.WriteLine ("例外をそのまま投げる.");
            throw;
        }
    }

    static void Main () {
        // 1.Threadの場合は例外が発生する
        Thread t = new Thread (new ThreadStart (Lance));
        t.IsBackground = true;
        t.Start ();

        Console.WriteLine ("スレッドで発生した例外はUnhandled.");
        Thread.Sleep (1000);
        
        // 2.Delegate.BeginInvokeはEndInvokeを呼ばないと
        // 例外がスレッドプールに食われる
        SilverSkin ss = new SilverSkin (Lance);
        IAsyncResult r = ss.BeginInvoke (null, null);
        r.AsyncWaitHandle.WaitOne ();

        Console.WriteLine ("BeginInvokeだけならUnhandledにならない.");
        Thread.Sleep (1000);

        // 3.Delegate.EndInvokeを呼ぶと、例外が受けられる
        r = ss.BeginInvoke (null, null);
        r.AsyncWaitHandle.WaitOne ();
        try {
            ss.EndInvoke (r);
        }
        catch (Exception e) {
            Console.WriteLine ("EndInvoke from Main.");
        }
        Console.WriteLine ("EndInvokeを呼ぶと例外が飛ぶ.");
        Thread.Sleep (1000);

        // 4. Callback内の例外はスレッドプールに食われる
        r = ss.BeginInvoke (new AsyncCallback (Callback), null);
        r.AsyncWaitHandle.WaitOne ();
        Console.WriteLine ("Callback内で例外が発生してもUnhandledにならない.");
        Thread.Sleep (1000);
    }
}
/* 結果
Unhandled Exception: System.Exception: 届け…
in <0x00024> Program:Lance ()
in (wrapper delegate-invoke) System.MulticastDelegate:invoke_void ()
スレッドで発生した例外はUnhandled.
BeginInvokeだけならUnhandledにならない.
EndInvoke from Main.
EndInvokeを呼ぶと例外が飛ぶ.
In Callback.
例外をそのまま投げる.
Callback内で例外が発生してもUnhandledにならない.
 */
  1. 通常のスレッドで例外が発生すれば、どこにもキャッチされない
  2. BeginInvokeを呼びっぱなしの場合、例外は闇に葬られます
  3. EndInvokeでスレッドから投げられた例外が飛ばされます
  4. Callbackはスレッドプール側なので、例外は闇に葬られます

例に出していませんがTimerイベントで飛ばした例外もスレッドプール側なので同じ運命です。