TaskScheduler.UnobservedTaskException never gets called
Based on my research, I have learned the following:
TaskScheduler.UnobservedTaskException
must wait for the task to be garbage collected before that task's unobserved exception will bubble up to theUnobservedTaskException
event.- If you're using
Task.Wait()
, it'll never get called anyway, because you're blocking on an impending result from the Task, hence the exception will be thrown onTask.Wait()
rather than bubble up to theUnobservedException
event. - Calling
GC.Collect()
manually is generally a bad idea unless you know exactly what you're doing, hence it's good in this case for confirming things, but not as a proper solution to the issue.
The Problem
If my application exits before the garbage collector kicks in,开发者_高级运维 I absolutely 100% cannot get my UnobservedTaskException
event to fire.
Note the following code:
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");
}
}
No exception file is written and nothing is written to the console. 10-15 minutes and more can go by after the application exits and still I see no evidence that my application's remains were garbage collected. You might ask, well why not just collect on exit? Well, my real world scenario is that my exception trapping runs inside a WCF service hosted inside a Windows service. I cannot trap when the Windows service is shutting down (and hence manually call GC.Collect()
) because there is no event for that as far as I can see.
Where am I going wrong? How do I ensure that if something deep inside the WCF service is going to ultimately break my windows service, that I have a chance to log the exception before the service falls over?
To me, TaskScheduler.UnobservedTaskException at first gives a very wrong sense of security. It's really not worth much if it depends on Garbage Collection.
I found the following solution, taken from this msdn article, to be much more reliable. It basically executes the continuation block (where you log the exception) only if there were unhandled exceptions in task1, and does not block UI execution.
You might also want to flatten nested AggregateExceptions and perhaps create a extension method, as 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);
Nathan,
You're points are all true. Try the following:
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");
}
}
}
I have noticed that the debugger often "traps" the UnobservedTaskException event, causing it to not fire appropriately. Run this outside of the debugger, and it will print "UNOBSERVED EXCEPTION" every time prior to shutting down.
精彩评论