永远不会调用TaskScheduler.UnobservedTaskException

时间:2010-10-19 22:33:11

标签: .net exception garbage-collection

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

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

    如果我的应用程序在垃圾收集器启动之前退出,我绝对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");
        }
    }
    

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

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

2 个答案:

答案 0 :(得分:7)

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

我发现以下解决方案取自this msdn article,更加可靠。它基本上只在task1中存在未处理的异常时执行延续块(在那里记录异常),并且不阻止UI执行。

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

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

答案 1 :(得分: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");
        }
    }
}

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