4

我几年前实现的基本 ViewModel 提供了这种扩展方法来运行任务,同时保持 UI 响应*:

protected void Work(Action job)
{
    IsBusy = true;
    var stackTrace = new StackTrace();
    var task = Task.Factory.StartNew(job)
        .ContinueWith(failedTask => HandleException(failedTask, stackTrace), 
                      TaskContinuationOptions.OnlyOnFaulted)
        .ContinueWith(_ => { IsBusy = false; });
}

void HandleException(Task task, StackTrace stackTrace)
{
    Dispatcher.BeginInvoke(
        () => { throw new Exception(task.Exception.InnerException.ToString() + 
                                    stackTrace); });
}

IsBusy是从 UI 观察到的用于显示进度条的属性。

背后的想法HandleExceptions是观察它们,然后将它们扔到 UI 线程中,被方法try/catch中的块捕获Main()并记录,然后向用户显示友好的消息并安全地关闭应用程序。传递了 stackTrace,因此日志包含调用者信息。

然而,我最近开始收到关于应用程序崩溃的报告,没有记录,也没有友好的消息,使用这个 Windows 对话框:

碰撞

查看 Windows 事件日志,我们得到了这个 EventData:

应用程序:xxxxApplication.Loader.exe

框架版本:v4.0.30319

说明:进程因未处理的异常而终止。

异常信息:System.AggregateException

堆栈:在 System.Threading.Tasks.TaskExceptionHolder.Finalize()

我做错了ContinueWith()什么吗?

任务异常是否有可能以某种方式未被观察到?


*:我知道BackgroundWorker。这在当时似乎是一个更好的主意,或者有额外的好处。

4

3 回答 3

1

我建议您处理AppDomain.CurrentDomain.UnhandledException 以显示您的“友好消息”,或者(如果这不是您的解决方案)您至少可以在此事件中记录错误,然后查看堆栈跟踪是什么来确定在哪里发生此“未处理”任务异常。

于 2012-10-27T10:04:37.017 回答
1

我的猜测是你在这里有一个竞争条件。来自Task.Exception参考:

引发未处理异常的任务存储生成的异常,并在调用 Wait 或访问 Exception 属性时将其传播到 AggregateException 中。在任务实例被垃圾收集时未观察到的任何异常都将在终结器线程上传播。有关更多信息和示例,请参阅异常处理(任务并行库)。

底线,如果您的任务在传入的委托Dispatcher.BeginInvoke被调用之前被收集,则将在主线程中引发异常。快速修复:

void HandleException(Task task, StackTrace stackTrace)
{
    // "observe" the exception
    var ex = task.Exception;
    Dispatcher.BeginInvoke(
        () => { throw new Exception(ex.InnerException.ToString() + 
                                    stackTrace); });
}
于 2013-05-30T13:25:33.610 回答
0

我不太确定,但是您将通过failedTask变量获得原始异常。如果任务失败,它提供非空值。AggregateException

该异常又具有一个Handle 方法,您可以提供一个委托,您可以在其中处理 AggregateException 中包含的每个异常,并说明您是否认为该异常已处理。

换句话说,如果您想在 Dispatcher 线程上重新抛出实际异常,我认为您应该确保在离开任务链之前将原始异常标记为已处理。

于 2012-10-22T13:51:36.527 回答