在Task.Run中的全局错误处理

3
我有一个 WPF C# 应用程序。
通常我使用全局错误处理器来捕获所有的错误:
private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    try
    {
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Xceed.Wpf.Toolkit.MessageBox.Show(e.Exception.ToString(), "Error",
          MessageBoxButton.OK, MessageBoxImage.Error)));
        e.Handled = true;
        InformedWorkerDataService.Common.Shared.RecordMessage(e.Exception.ToString(), true);
    }
    finally { }
}

然而,如果启动一个任务运行一部分代码并且它抛出一个错误,我观察到该错误不会被捕获。
Task.Run(() =>
{
    throw and error here    
});

所以我必须加一个“Try-Catch”来捕获它:
Task.Run(() =>
{
    try
    {
        throw an error here
    }
    catch (Exception ex)
    {
        do  something with error
    }
});

"

~ which defeats the object of having a Global Error handler

"的意思是“这就违背了拥有全局错误处理程序的初衷”。
“我应该怎么做?”

2
尝试使用 TaskScheduler.UnobservedTaskException 事件。 - dkozl
@dkozl 很酷 - 谢谢 :) - Andrew Simpson
2个回答

5

您可以使用TaskScheduler.UnobservedTaskException事件来处理未观察到的任务异常。

TaskScheduler.UnobservedTaskException += (s, e) => {
    e.Exception  //The Exception that went unobserved.
    e.SetObserved(); //Marks the Exception as "observed," thus preventing it from triggering exception escalation policy which, by default, terminates the process.
};

当一个出现故障的任务未被观察到的异常即将触发异常升级策略时,就会发生这种情况。默认情况下,异常升级策略会终止进程。

这个应用程序域范围内的事件提供了一种机制来防止异常升级策略(默认情况下终止进程)的触发。


我很高兴你发了一个答案,这样我就可以接受它了。那我还需要设置这个吗:<ThrowUnobservedTaskExceptions enabled="true"/>? - Andrew Simpson
如果你处理了异常,那就不会出现这种情况。通常我不设置ThrowUnobservedTaskExceptions。如果有异常,我宁愿处理/记录它而不是隐藏它。 - dkozl
1
我强烈建议,如果您希望在异常发生时显示UI通知,请不要依赖于UnobservedTaskException。根据我的经验,此处理程序不会实时运行,并且将在异常发生后很长时间才显示消息框。显然,堆栈跟踪等仍然是正确的,但用户体验非常混乱。除了真正意外的异常之外,我仍然建议使用内联catch。 - Andrew Hanlon
1
@AndrewHanlon 刚刚看到了你的评论。我也遇到过这种情况。你说得对,在这种情况下保持它的内联。谢谢。 - Andrew Simpson

2

如我在接受的答案评论中提到的,TaskScheduler.UnobservedTaskException不能保证在抛出异常时实时触发。这意味着将此处理程序用于用户通知可能会非常令人困惑,因为用户操作和错误通知不会同步发生。对于“意外”任务异常的用户驱动处理,您可以创建以下辅助方法并使用TaskEx.Run而不是Task.Run

public static class TaskEx
{
    public static Task Run(Action function)
    {
        return Task.Run(() =>
        {
            try
            {
                function();
            }
            catch (Exception ex)
            {
                TraceEx.TraceException(ex);
                //Dispatch your MessageBox etc.
            }
        });
    }
}

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