为什么不要使用 async void?
问题
在使用 Abp 框架的后台作业时,当后台作业抛出异常,会导致整个程序崩溃。在 Abp 框架的底层执行后台作业的时候,有 try/catch
语句块用来捕获后台任务执行时的异常,但是在这里没有生效。
原始代码如下:
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 都是放在线程池执行的。
所以在上述样例当中,此时 AsyncVoidMethodBuilder.Create()
使用的同步上下文为 null
,这个时候 ThreadPool
就不会捕获异常给原有线程处理,而是直接抛出。
线程池在底层使用 AsyncVoidMethodBuilder.Craete()
所拿到的同步上下文,所捕获异常的代码如下:
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
来监听异常,不过你是没办法从异常状态恢复的。
参考文章:
Stephen Cleary: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
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(); } }
原文地址:https://www.cnblogs.com/myzony/p/10647460.html
相关推荐
-
WPF StringFormat 格式化文本 C#
2019-6-14
-
Identity Server 4 预备知识 — OAuth 2.0 简介 C#
2019-4-15
-
.NET Core HttpClient调用腾讯云对象存储Web API的”ERROR_CGI_PARAM_NO_SUCH_OP”问题 C#
2019-5-20
-
.NET Core中的数据保护组件 C#
2019-4-2
-
CSharp工程中的几个文件 C#
2019-8-29
-
使用websocket-sharp来创建c#版本的websocket服务 C#
2019-5-15
-
在Mac下运行ASP.NET Core应用程序 C#
2019-8-30
-
让 .NET 更方便的导入导出 Excel C#
2019-8-30
-
C#常用修饰符 C#
2019-5-17
-
Web Api 2.0中使用Swagger生成Api文档的2个小Tips C#
2019-5-5