web-dev-qa-db-ja.com

TaskScheduler.UnobservedTaskExceptionイベントハンドラーがトリガーされることはありません

C#タスク並列ライブラリに関する本を読んでいて、次の例がありますが、TaskScheduler.UnobservedTaskExceptionハンドラーがトリガーされることはありません。誰かが私に理由について何か手がかりを与えることができますか?

TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) =>
{
    eventArgs.SetObserved();
    ((AggregateException)eventArgs.Exception).Handle(ex =>
    {
        Console.WriteLine("Exception type: {0}", ex.GetType());
        return true;
    });
};

Task task1 = new Task(() => 
{
    throw new ArgumentNullException();
});

Task task2 = new Task(() => {
    throw new ArgumentOutOfRangeException();
});

task1.Start();
task2.Start();

while (!task1.IsCompleted || !task2.IsCompleted)
{
    Thread.Sleep( 5000 );
}

Console.WriteLine("done");
Console.ReadLine();
35
devlife

残念ながら、その例ではコードが表示されません。 UnobservedTaskExceptionは、タスクがGCによって収集され、例外が監視されない場合にのみ発生します。_task1_および_task2_への参照を保持している限り、GCは収集せず、例外ハンドラは表示されません。

UnobservedTaskExceptionの動作を確認するために、次のことを試してみます(考案された例)。

_public static void Main()
{
    TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) =>
                {
                    eventArgs.SetObserved();
                    ((AggregateException)eventArgs.Exception).Handle(ex =>
                    {
                        Console.WriteLine("Exception type: {0}", ex.GetType());
                        return true;
                    });
                };

    Task.Factory.StartNew(() =>
    {
        throw new ArgumentNullException();
    });

    Task.Factory.StartNew(() =>
    {
        throw new ArgumentOutOfRangeException();
    });


    Thread.Sleep(100);
    GC.Collect();
    GC.WaitForPendingFinalizers();

    Console.WriteLine("Done");
    Console.ReadKey();
}
_

これにより、メッセージが表示されます。最初のThread.Sleep(100)呼び出しは、タスクがスローするのに十分な時間を提供します。 collect and waitはGCコレクションを強制し、イベントハンドラーを2回起動します。

54
Reed Copsey

そのサンプルスニペットでは、例外は「監視されない」ことはありません。ガベージコレクターがタスクインスタンスを削除するまでは。次のように書き直す必要があります。

class Program {
    static void Main(string[] args) {

        TaskScheduler.UnobservedTaskException += ( object sender, UnobservedTaskExceptionEventArgs eventArgs ) =>
        {
            eventArgs.SetObserved();
            ( (AggregateException)eventArgs.Exception ).Handle( ex =>
            {
                Console.WriteLine("Exception type: {0}", ex.GetType());
                return true;
            } );
        };

        Run();

        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("done");
        Console.ReadLine();
    }

    static void Run() {
        Task task1 = new Task(() => {
            throw new ArgumentNullException();
        });

        Task task2 = new Task(() => {
            throw new ArgumentOutOfRangeException();
        });

        task1.Start();
        task2.Start();

        while (!task1.IsCompleted || !task2.IsCompleted) {
            Thread.Sleep(50);
        }
    }
}

これを行わないでください。Task.Wait()を使用してください。

4
Hans Passant

このコードを.NET4.5、Visual Studio 2012(デバッグまたはリリース、関係ありません)で実行し、notput ThrowUnobservedTaskException 私のapp.configで:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Test
{
    public static class Program
    {
        private static void Main()
        {
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

            RunTask();

            Thread.Sleep(2000);

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Console.ReadLine();
        }

        static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            Console.WriteLine("Caught!");
        }

        private static void RunTask()
        {
            Task<int> task = Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000); // emulate some calculation
                Console.WriteLine("Before exception");
                throw new Exception();
                return 1;
            });
        }
    }
}

そして、例外はUnobservedTaskExceptionハンドラーによってキャッチされます(「Caught!」が出力されます)。

0
nightcoder