在使用 Abp 框架的後臺做業時,當後臺做業拋出異常,會致使整個程序崩潰。在 Abp 框架的底層執行後臺做業的時候,有 try/catch
語句塊用來捕獲後臺任務執行時的異常,可是在這裏沒有生效。html
原始代碼以下:框架
public class TestAppService : ITestAppService { private readonly IBackgroundJobManager _backgroundJobManager; public TestAppService(IBackgroundJobManager backgroundJobManager) { _backgroundJobManager = backgroundJobManager; } public Task GetInvalidOperationException() { throw new InvalidOperationException("模擬無效操做異常。"); } public async Task<string> EnqueueJob() { await _backgroundJobManager.EnqueueAsync<BG, string>("測試文本。"); return "執行完成。"; } } public class BG : BackgroundJob<string>, ITransientDependency { private readonly TestAppService _testAppService; public BG(TestAppService testAppService) { _testAppService = testAppService; } public override async void Execute(string args) { await _testAppService.GetInvalidOperationException(); } }
調用接口時的效果:異步
出現這種狀況是由於任何異步方法返回 void
時,拋出的異常都會在 async void
方法啓動時,處於激活狀態的同步上下文 (SynchronizationContext
) 觸發,咱們的全部 Task 都是放在線程池執行的。async
因此在上述樣例當中,此時 AsyncVoidMethodBuilder.Create()
使用的同步上下文爲 null
,這個時候 ThreadPool
就不會捕獲異常給原有線程處理,而是直接拋出。ide
線程池在底層使用 AsyncVoidMethodBuilder.Craete()
所拿到的同步上下文,所捕獲異常的代碼以下:post
internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext) { var edi = ExceptionDispatchInfo.Capture(exception); // 同步上下文是空的,則不會作處理。 if (targetContext != null) { try { targetContext.Post(state => ((ExceptionDispatchInfo)state).Throw(), edi); return; } catch (Exception postException) { edi = ExceptionDispatchInfo.Capture(new AggregateException(exception, postException)); } } }
雖然你能夠經過掛載 AppDoamin.Current.UnhandledException
來監聽異常,不過你是沒辦法從異常狀態恢復的。測試
參考文章:ui
Stephen Cleary: https://msdn.microsoft.com/en-us/magazine/jj991977.aspxspa
Jerome Laban's:https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html線程
能夠使用 AsyncBackgroundJob<TArgs>
替換掉以前的 BackgroundJob<TArgs>
,只須要實現它的 Task ExecuteAsync(TArgs args)
方法便可。
public class BGAsync : AsyncBackgroundJob<string>,ITransientDependency { private readonly TestAppService _testAppService; public BGAsync(TestAppService testAppService) { _testAppService = testAppService; } protected override async Task ExecuteAsync(string args) { await _testAppService.GetInvalidOperationException(); } }