永远不会触发TaskScheduler.UnobservedTaskException事件处理程序

dev*_*ife 34 c# task-parallel-library

我正在阅读一本关于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();
Run Code Online (Sandbox Code Playgroud)

Ree*_*sey 53

不幸的是,该示例永远不会向您展示您的代码.该UnobservedTaskException如果一个任务被用一个异常未观测到的GC收集才会发生-只要你坚持一个参考task1task2时,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();
}
Run Code Online (Sandbox Code Playgroud)

这将显示您的消息.第一个Thread.Sleep(100)调用为任务提供了足够的时间.收集和等待强制GC集合,它将激活您的事件处理程序2x.

  • 此操作不需要ThrowUnobservedTaskException.只有当您想要恢复到终止进程的Task异常的4.0行为时,才需要它.在4.5中,默认值更改为不终止进程.事件处理程序捕获未观察到的异常不是必需的.备注中的更多信息[这里](https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.unobservedtaskexception(v = vs.110).aspx) (8认同)
  • 在.NET 4.5中,您需要额外的伏都教才能实现此目的.首先,您需要在app.config中启用[ThrowUnobservedTaskException](http://msdn.microsoft.com/en-GB/library/jj160346.aspx),其次,您需要在Release配置中构建代码(一个仅在链接的MSDN文章的示例部分中记录的点).我在浏览器中责怪这个"让我们默默地抑制异常"的心态:) (6认同)
  • @romkyns,我不需要为我的.NET 4.5应用程序执行此操作. (2认同)
  • 我在 Core 3.0 中尝试了这段代码。它不会触发任何事件。要么此代码中缺少某些内容,要么整个应用程序需要额外的配置/设置。或者也许这个机制只是有时有效。 (2认同)

Han*_*ant 5

该示例片段中不会“未观察到”异常。直到垃圾收集器清除任务实例。你必须像这样重写它:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不要这样做,请使用 Task.Wait()。