捕获任务中的异常

时间:2012-10-22 13:36:33

标签: .net wpf task-parallel-library

几年前实现的我的基本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对话框获取有关应用程序崩溃的报告,而不会记录,也不会发送友好消息:

crash

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

  

应用程序:xxxxApplication.Loader.exe

     

框架版本:v4.0.30319

     

描述:由于未处理的异常,该进程已终止。

     

异常信息:System.AggregateException

     

Stack:at System.Threading.Tasks.TaskExceptionHolder.Finalize()

我对ContinueWith()做错了什么?

是否可能以某种方式保留任务异常?


<子> *:我知道BackgroundWorker。这可能在当时看起来更好,或者有其他好处。

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

我的猜测是你在这里遇到了竞争条件。来自Task.Exception参考:

  

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

Bottomline,如果在调用传入Dispatcher.BeginInvoke的委托之前收集了您的任务,则会在主线程中抛出异常。快速修复:

void HandleException(Task task, StackTrace stackTrace)
{
    // "observe" the exception
    var ex = task.Exception;
    Dispatcher.BeginInvoke(
        () => { throw new Exception(ex.InnerException.ToString() + 
                                    stackTrace); });
}

答案 2 :(得分:0)

我不太确定,但你会通过failedTask变量获得原始异常。如果任务失败,则It provides为非空AggregateException

该异常反过来为Handle method,您可以在其中提供一个委托,您可以在其中处理AggregateException中包含的每个异常,并说明您是否考虑处理异常。

换句话说,如果你想在Dispatcher线程上重新抛出实际的异常,我认为你应该确保在离开Task链之前将原始异常标记为 handling