当你在一个Task执行中抛出异常,比如:
Task.Factory.StartNew(() => { throw new Exception(); });
运行该方法,没有任何异常抛出。
事实上此时Task的异常处于未觉察状态,这个未觉察状态的异常会在垃圾回收时终结器执行线程中被抛出。
为了诱发这个异常,我们可以通过GC.Collect来强制垃圾回收从而引发终结器处理线程,此时Task的未觉察异常会被抛出。
//在Task中抛出异常 Task.Factory.StartNew(() => { throw new Exception(); }); //确保任务完成 Thread.Sleep(100); //强制垃圾会受到 GC.Collect(); //等待终结器处理 GC.WaitForPendingFinalizers();
OK,异常抛出,程序崩溃,如下输出:
Unhandled Exception: System.AggregateException: A Task's exception(s) were not bserved either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> Sys em.Exception: Exception of type 'System.Exception' was thrown. at Mgen.Program.<Main>b__0() in E:\Users\Mgen\Documents\Visual Studio 2010\P ojects\Mgen\Mgen\Program.cs:line 19 at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.Execute() --- End of inner exception stack trace --- at System.Threading.Tasks.TaskExceptionHolder.Finalize()
我们可以通过调用Task.Wait/WaitAll,或者引用Task<T>.Result属性,或者最简单的引用Task.Exception属性来使Task的异常被觉察。比如这样:
通过Task.Wait手动捕获AggregateException:
try { Task.WaitAll( Task.Factory.StartNew(() => { throw new Exception(); })); } catch (AggregateException) { } //确保任务完成 Thread.Sleep(100); //强制垃圾会受到 GC.Collect(); //等待终结器处理 GC.WaitForPendingFinalizers();
这样就不会有任何异常抛出(即使是终结器线程已经结束)。
当然最简单的就是直接引用一下Task.Exception属性:
注意这里使用Task.ContinueWith是为了避免直接引用Task变量,这样垃圾回收可以处理这个Task对象!
//使用Task.ContinueWith可以避免直接引用Task变量,这样垃圾回收可以处理这个Task对象! Task.Factory.StartNew(() => { throw new Exception(); }).ContinueWith(t => { var exp = t.Exception; }); //确保任务完成 Thread.Sleep(100); //强制垃圾会受到 GC.Collect(); //等待终结器处理 GC.WaitForPendingFinalizers();
同样不会有异常抛出。
另外,可以通过TaskContinuationOptions.OnlyOnFaulted来使引用Exception属性只发生在发生异常时(即Exception为null的时候没必要再去引用它),代码:
Task.Factory.StartNew(() => { throw new Exception(); }).ContinueWith(t => { var exp = t.Exception; }, TaskContinuationOptions.OnlyOnFaulted);
最后是TaskScheduler.UnobservedTaskException事件,该事件是所有未觉察异常被抛出前的最后可以将其觉察的方法。通过UnobservedTaskExceptionEventArgs.SetObserved方法来将异常标记为已觉察。
代码:
TaskScheduler.UnobservedTaskException += (s, e) => { //设置所有未觉察异常被觉察 e.SetObserved(); }; Task.Factory.StartNew(() => { throw new Exception(); }); //确保任务完成 Thread.Sleep(100); //强制垃圾会受到 GC.Collect(); //等待终结器处理 GC.WaitForPendingFinalizers();
OK,没有异常抛出。
转载自:http://www.mgenware.com/blog/?p=231