永远不会调用TaskScheduler.UnobservedTaskException

Nat*_*ley 13 .net garbage-collection exception

根据我的研究,我学到了以下内容:

  1. TaskScheduler.UnobservedTaskException必须等待该任务被垃圾收集,然后该任务的未被观察到的异常将冒泡到该UnobservedTaskException事件.
  2. 如果你正在使用Task.Wait()它,它永远不会被调用,因为你正在阻止来自Task的即将发生的结果,因此异常将被抛出Task.Wait()而不是冒泡到UnobservedException事件.
  3. GC.Collect()手动调用通常是一个坏主意,除非你确切地知道你正在做什么,因此在这种情况下确认事情是好的,但不能作为问题的正确解决方案.

问题

如果我的应用程序在垃圾收集器启动之前退出,我绝对100%无法UnobservedTaskException触发我的事件.

请注意以下代码:

class Program
{
    static void Main(string[] args)
    {
        TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

        Task.Factory.StartNew(() =>
        {
            Console.WriteLine("Task started.");
            throw new Exception("Test Exception");
        });

        Thread.Sleep(1000);
        //GC.Collect();
        //GC.WaitForPendingFinalizers();
    }

    static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        File.WriteAllText(@"C:\data\TestException.txt", e.Exception.ToString());
        Console.WriteLine("UNOBSERVED EXCEPTION");
    }
}
Run Code Online (Sandbox Code Playgroud)

没有写入异常文件,也没有任何内容写入控制台.在应用程序退出后10-15分钟甚至更长时间可以继续,但我仍然看不到我的应用程序的遗骸被垃圾收集的证据.您可能会问,为什么不在退出时收集?好吧,我的真实场景是我的异常陷阱在Windows服务中托管的WCF服务内运行.当Windows服务关闭(因此手动调用GC.Collect())时,我无法陷阱,因为就我所见,没有任何事件.

我哪里错了?我如何确保如果WCF服务内部的某些内容最终会破坏我的Windows服务,那么我有机会在服务崩溃之前记录异常?

Mik*_*chs 8

对我来说,TaskScheduler.UnobservedTaskException最初给出了非常错误的安全感.如果它取决于垃圾收集,它真的不值得.

我发现从这篇msdn文章中获得的以下解决方案更加可靠.它基本上只在task1中存在未处理的异常时才执行延续块(在那里记录异常),并且不阻止UI执行.

您可能还想展平嵌套的AggregateExceptions,并可能创建一个扩展方法,如Reed Copsey所示.

var task1 = Task.Factory.StartNew(() =>
{
    throw new MyCustomException("Task1 faulted.");
})
.ContinueWith((t) =>
{
    Console.WriteLine("I have observed a {0}",
        t.Exception.InnerException.GetType().Name);
},
TaskContinuationOptions.OnlyOnFaulted);
Run Code Online (Sandbox Code Playgroud)


Ree*_*sey 2

内森,

你说的都是真的。请尝试以下操作:

namespace ConsoleApplication1
{
    using System;
    using System.Threading;
    using System.Threading.Tasks;

    class Program
    {
        static void Main(string[] args)
        {
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

            Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Task started.");
                throw new Exception("Test Exception");
            });

            Thread.Sleep(1000);
            Console.WriteLine("First Collect");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine("Waiting");
            Console.ReadKey();
        }

        static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            Console.WriteLine("UNOBSERVED EXCEPTION");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我注意到调试器经常“捕获”UnobservedTaskException 事件,导致它无法正确触发。在调试器之外运行它,每次关闭之前都会打印“UNOBSERVED EXCEPTION”。