一个任务的异常没有被等待该任务或访问其异常属性所观察到。因此,未观察到的异常已经...

111
这是什么意思,如何解决?
我正在使用TPL任务。
整个错误信息:
“一个任务的异常未被等待或访问其异常属性。 因此,未观察到的异常被终结线程重新抛出。”
“在System.Threading.Tasks.TaskExceptionHolder.Finalize()处”
“mscorlib”
2个回答

172
如果您创建了一个任务,并且从未调用task.Wait()或尝试检索Task<T>的结果,在垃圾收集器收集任务时,它将在 finalization 期间拆除您的应用程序。有关详细信息,请参阅 MSDN 中有关TPL中的异常处理的页面。
在这种情况下,最好的选择是“处理”异常。这可以通过添加延续来完成 - 您可以将延续附加到任务上,并记录/忽略等发生的异常。这提供了一种清晰的方式来记录任务异常,并且可以编写为简单的扩展方法,例如:
public static void LogExceptions(this Task task)
{
    task.ContinueWith( t =>
    {
         var aggException = t.Exception.Flatten();
         foreach(var exception in aggException.InnerExceptions)
             LogException(exception);
    }, 
    TaskContinuationOptions.OnlyOnFaulted);
}

通过上述方法,您可以通过以下方式防止任何任务拆除应用程序并记录它:
Task.Factory.StartNew( () => 
   { 
       // Do your work...
   }).LogExceptions();

或者,您可以订阅TaskScheduler.UnobservedTaskException并在那里处理它。


20
为了增加娱乐性,在一个名为四个字母单词的类中添加一个静态存根方法Off,并将其用作通用续集。这有助于缓解因此特定异常而产生的一些积压的挫败感。 - Aaronaught
1
在调用ContinueWith之前,任务是否可能会抛出异常? - Tim Sylvester
1
@TimSylvester 即使发生在连接之前,该框架仍将通过连续性进行映射。 - Reed Copsey
1
@MasonG.Zhwiti 扩展方法必须在静态类中定义,并且命名空间必须包含(using TheNamespace;). - Reed Copsey
35
重要提示:此项仅适用于.Net 4.0版本。异常处理在.net 4.5版本中默认已更改为不会导致应用程序崩溃。有关更多信息,请参阅.NET 4.5中的任务异常处理 - i3arnon
显示剩余10条评论

47

当任务被垃圾回收后,该任务本身失败了,这意味着任务已经完成。有两种解决方法:

  • 直接处理任务的失败情况(使用ContinueWith(...)进行订阅,在参数中检查Task.IsFaulted.Exception
  • 处理TaskScheduler.UnobservedTaskException事件,并标记它为已观察(在记录错误后调用e.SetObserved()

4
如果您的连续操作只是检查 IsFaulted,则可以使用 OnlyOnFaulted 连续选项,避免手动检查。+1 - 带一个补充 - 如果您的连续操作只是检查 IsFaulted,则可以使用 OnlyOnFaulted 连续选项,避免手动检查... - Reed Copsey
实际上,我在 tpl 任务中调用了一个公共静态函数,这导致了问题。使用 try catch 可以解决这个问题吗?我真的需要创建另一个任务并等待它吗?谢谢。 - Furkan Gözükara
5
感谢您提到需要在UnobservedTaskExceptionEventArgs上调用SetObserved方法。 - James Webster
提醒一下,设置OnlyOnFaulted意味着如果附加到的任务没有故障,则OnlyOnFaulted任务将被标记为已取消。而取消状态会带来自己的麻烦。 - Robert Christ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接