TaskScheduler.UnobservedTaskException event handler never being triggered

TaskScheduler.UnobservedTaskException event handler never being triggered

问题

I'm reading through a book about the C# Task Parallel Library and have the following example but the TaskScheduler.UnobservedTaskException handler is never being triggered. Can anyone give me any clues as to why?

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();

 

回答

Unfortunately, that example will never show you your code. The UnobservedTaskException will only happen if a Task gets collected by the GC with an exception unobserved - as long as you hold a reference to task1 and task2, the GC will never collect, and you'll never see your exception handler.

In order to see the behavior of the UnobservedTaskException in action, I'd try the following (contrived example):

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();
}

This will show you your messages. The first Thread.Sleep(100) call provides enough time for the tasks to throw. The collect and wait forces a GC collection, which will fire your event handler 2x.

评论

In .NET 4.5, you need extra voodoo for this to happen. First, you need to enable ThrowUnobservedTaskException in the app.config, and second, you need to build the code in the Release configuration (a point that's only documented in the Example section of the linked MSDN article). I blame this "let's suppress exceptions silently" mentality on JavaScript in browsers :) Apr 30, 2013 at 14:05
 
ThrowUnobservedTaskException is not required for this to work. It is only necessary if you want to revert to the 4.0 behavior of a Task exception terminating the process. In 4.5 the default changed to not terminate the process. It is not required for the unobserved exception to be caught by the event handler. More info in the remarks here Sep 21, 2015 at 22:48
 
 
 

 

作者:Chuck Lu    GitHub    
posted @   ChuckLu  阅读(100)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2021-07-02 Msg 547, Level 16, State
2021-07-02 Win10操作系统升级到20H2的进程名字
2021-07-02 Using Spy++ in Visual Studio 2019
2021-07-02 How to print GETDATE() in SQL Server with milliseconds in time?
2020-07-02 NLog Tutorial
2020-07-02 Securing Azure CDN assets with token authentication
2020-07-02 How can I uninstall the language pack of .net 4.8?
点击右上角即可分享
微信分享提示