如何将Task.Run()中的异常传递至主线程以实现全局处理?
问题描述
我正尝试实现一个通用方法来异步处理所有API调用并处理异常。如何将Task.Run()内部的异常传递到主线程,以便在未处理异常中进行全局处理?
我的全局异常处理器如下:
AppDomain.CurrentDomain.UnhandledException += LogUnhandledCurrentDomainException; Dispatcher.UnhandledException += OnDispatcherUnhandledException;
异步API调用的通用方法:
public async Task<T> InvokeApiAsync<T>(Func<T> function) { return await Task.Run(function); }
我通过WPF视图模型中的方法调用该异步函数:
private async Task LoadDocuments() { IsBusy = true; Documents = await _serviceInvoker.InvokeApiAsync(() => _blobService.GetBlobs().ToList()); IsBusy = false; }
上述方法是从Caliburn.Micro的事件中调用的:
protected override async Task OnInitializeAsync(CancellationToken cancellationToken) { await LoadDocuments(); await base.OnInitializeAsync(cancellationToken); }
解决方案
1. 补充TaskScheduler.UnobservedTaskException事件监听
你的代码中,await Task.Run(function)已经会将内部异常重新抛回主线程的调用上下文,但**async Task方法的未处理异常不会触发AppDomain.CurrentDomain.UnhandledException**,而是会触发TaskScheduler.UnobservedTaskException事件。需要添加这个事件的处理器来捕获这类异常:
TaskScheduler.UnobservedTaskException += LogUnobservedTaskException; // 对应的处理器示例 private void LogUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { // 展开嵌套异常并记录日志 var targetException = e.Exception.Flatten(); // 标记异常为已观察(可选,避免应用因未处理任务异常崩溃) e.SetObserved(); }
2. 修复LoadDocuments的状态重置逻辑
当前代码中,如果API调用抛出异常,IsBusy = false不会执行,导致UI一直处于忙碌状态。建议用try/finally块确保状态重置:
private async Task LoadDocuments() { IsBusy = true; try { Documents = await _serviceInvoker.InvokeApiAsync(() => _blobService.GetBlobs().ToList()); } finally { IsBusy = false; } }
3. 关于Caliburn.Micro的异步方法处理
Caliburn.Micro会正确处理OnInitializeAsync这类异步生命周期方法的返回任务,但如果该方法抛出异常,框架不会自动将其冒泡到全局异常处理器。此时未处理的异常会被包装到Task中,最终触发TaskScheduler.UnobservedTaskException事件,通过步骤1添加的处理器即可捕获。
内容的提问来源于stack exchange,提问作者Vinay
相关产品推荐
相关产品推荐

