【问题标题】:How to capture and handle unhandled exceptions that happens inside Task.Factory如何捕获和处理 Task.Factory 内部发生的未处理异常
【发布时间】:2020-05-25 17:55:01
【问题描述】:

我已经在应用程序启动中对此进行了编码

App.xaml.cs

public App()
{
    AppDomain currentDomain = AppDomain.CurrentDomain;

    currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);

    DispatcherUnhandledException += App_DispatcherUnhandledException;

    Application.Current.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(Application_DispatcherUnhandledException);

    TaskScheduler.UnobservedTaskException += new EventHandler<UnobservedTaskExceptionEventArgs>(Application_DispatcherUnhandledException2);

    this.Dispatcher.UnhandledException += Dispatcher_UnhandledException;


}

private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    writeMessage(e.Exception);
}

private void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    writeMessage(e.Exception);
}

private static void writeMessage(Exception e)
{
    string srMsg = e.InnerException?.Message;

    if (srMsg != null)
    {
        srMsg += "\r\n\r\nStack\r\n" + e.InnerException?.StackTrace;
    }

    if (srMsg == null)
    {
        srMsg = e.Message + "\r\n\r\nStack\r\n" + e.StackTrace;
    }

    srMsg += "\r\n\r\n*********\r\n\r\n";

    File.AppendAllText("global_errors.txt", srMsg);

}

private static void Application_DispatcherUnhandledException2(object o, UnobservedTaskExceptionEventArgs e)
{
    writeMessage(e.Exception);
}

private static void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    writeMessage(e.Exception);
}

private static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
    Exception e = (Exception)args.ExceptionObject;
    writeMessage(e);
}

但是,这仍然没有捕获任务工厂错误

例如

此线程中的任何建议均无效WPF global exception handler

【问题讨论】:

  • 这不是一个未处理的异常。任务包装器捕获并保存异常。您可以尝试将按钮单击的签名更改为private async void Button_Click,然后执行await Task.Run(() =&gt; test.Example(););。虽然不能在这里测试它,所以不确定它是否会工作。这里注意,除非某些特殊情况,Task.RunTaskFactory.StartNew 的替换:devblogs.microsoft.com/pfxteam/…
  • 您看过AppDomain.FirstChanceException 事件吗?过去,我成功地在任何线程中捕获了“未处理的异常”。唯一的缺点是,无论您是否使用 try 和 catch,您都会得到 any 抛出的错误。
  • @Knoop 添加等待肯定会解决问题,但随后我的屏幕变得无响应,因为它锁定了主线程
  • @Twenty 它捕获了错误,但我不明白为什么会发生错误。输入字符串的格式不正确。堆栈在 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)。它显示消息而不是堆栈跟踪
  • 嗯,我自己测试了一下,我得到了 StackTrace 和 FirstChanceExceptionEventArgs。你确定e.Exception.StackTrace 为空吗?

标签: c# .net wpf exception task-parallel-library


【解决方案1】:

仅当您通过调用Wait().Result 或访问其Exception 属性来观察Task 时才会引发异常。

除非您使用 &lt;ThrowUnobservedTaskExceptions&gt; 配置元素恢复到 .NET Framework 4 中的默认行为并终止进程,否则不会引发未观察到的异常:

<runtime>
  <ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>

如果你真的想捕获异常,你应该在传递给Task.Factory.StartNew 的委托中进行。您可以按照here 的建议将此功能包装在扩展方法中。

【讨论】:

  • 它有一个很好的问题“这需要将 .Catch 块添加到 Task.Run 的每个调用中?有没有办法将它自动​​添加到所有调用中?”如果我必须为每项任务编写它不是全球性的:D
  • 另外添加 没有任何改变:/
  • @MonsterMMORPG:是的。 TaskScheduler.UnobservedTaskException 的事件处理程序在收集到未观察到的任务时被调用。
  • 故障任务的垃圾回收不是确定性的。它可能在短时间或长时间后发生。除非您在代码中显式调用 GC.Collect()
【解决方案2】:

可以通过多种方式获取 StackTrace,这实际上取决于最适合您需求的方式。无论如何,我建议您尝试以下方法。

  1. 在项目开始时为 AppDomain.FirstChanceException 事件添加回调,例如在您要捕获/记录的错误发生之前。这可能类似于以下内容:
public static Main()
{
    AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
}

private static void CurrentDomain_FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
{

}
  1. 之后,在CurrentDomain_FirstChanceException 方法中添加Environment.StackTrace。它将当前的StackTrace 保存为string。根据我的测试,它包括FileNameFileLineNumber
var currentStackTrace = Environment.StackTrace;

// Now it is on to you on what you want to do with the StackTrace. This could be some kind of logging.

请注意,这将记录 AppDomain 抛出的 ALL 异常,无论它们是否由代码处理,例如try/catch

【讨论】:

  • 还有一件事。我看到这也会捕获 try/catch 处理的。有没有办法知道异常是否被优雅地处理?这样我就不会记录它了
  • 很遗憾,您不能按照我在答案中所说的那样做。正如名字所暗示的FirstChanceException,它会在抛出异常的第一刻被执行,甚至在try/catch之前。您可以做的是创建某种自定义日志记录,这将允许您的应用程序告诉记录器某个错误已得到处理。例如,每次遇到异常时调用一个方法,然后 try/catch 处理它。
猜你喜欢
  • 1970-01-01
  • 2015-09-11
  • 2014-04-22
  • 2017-11-25
  • 2015-09-10
  • 2011-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多