在使用 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(); } }