我正在阅读一本关于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();
答案 0 :(得分:49)
不幸的是,该示例永远不会向您展示您的代码。 UnobservedTaskException
只有在GC收集任务并且未观察到异常时才会发生 - 只要您持有对task1
和task2
的引用,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();
}
这会显示您的消息。第一个Thread.Sleep(100)
调用为任务提供了足够的时间。收集和等待强制GC集合,它将激活您的事件处理程序2x。
答案 1 :(得分:4)
该示例代码段中的异常不会“未被观察”。直到垃圾收集器摆脱Task实例。你必须像这样重写它:
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);
}
}
}
不要这样做,请使用Task.Wait()。
答案 2 :(得分:0)
我在.NET 4.5,Visual Studio 2012(调试或发布,无关紧要)上运行此代码,我在我的app.config中不放置ThrowUnobservedTaskException:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Test
{
public static class Program
{
private static void Main()
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
RunTask();
Thread.Sleep(2000);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.ReadLine();
}
static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
Console.WriteLine("Caught!");
}
private static void RunTask()
{
Task<int> task = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000); // emulate some calculation
Console.WriteLine("Before exception");
throw new Exception();
return 1;
});
}
}
}
异常由UnobservedTaskException处理程序捕获(&#34; Caught!&#34;打印)。