为什么不要使用 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();
}
}
为什么不要使用 async void?的更多相关文章
- 为什么不要使用 Async Void ?
原文:为什么不要使用 Async Void ? 问题 在使用 Abp 框架的后台作业时,当后台作业抛出异常,会导致整个程序崩溃.在 Abp 框架的底层执行后台作业的时候,有 try/catch 语句块 ...
- 《C#并发编程经典实例》学习笔记—2.9 处理 async void 方法的异常
问题 需要处理从 async void 方法传递出来的异常. 解决方案 书中建议尽量不写 async void 这样的方法,如果非写不可,建议在方法内部 try catch 所有的代码,即在方法内部处 ...
- 处理async void 方法中无法捕捉异常信息
利用 NuGet库 Nito.AsyncEx 中的 AsyncContext类. 添加NuGet类库,使用AsyncContext AsyncContext.Run(Action action);
- [C#] async 的三大返回类型
async 的三大返回类型 序 博主简单数了下自己发布过的异步文章,已经断断续续 8 篇了,这次我想以 async 的返回类型为例,单独谈谈. 异步方法具有三个可让开发人员选择的返回类型:Task&l ...
- async & await 的前世今生(Updated)
async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...
- [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程
怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html ...
- [.NET] 利用 async & await 进行异步 IO 操作
利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html 序 上次,博主 ...
- await and async
Most people have already heard about the new “async” and “await” functionality coming in Visual Stud ...
- C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决
返回目录 关于死锁的原因 理解该死锁的原因在于理解await 处理contexts的方式,默认的,当一个未完成的Task 被await的时候,当前的上下文将在该Task完成的时候重新获得并继续执行剩余 ...
随机推荐
- 吐槽一下--最近多次在腾讯以及万科的面试经历---Web前端与PHP后端开发
前端时间,由于职业发展等,想要换一份工作,于是投递了一些国内还算知名的公司,列如: 腾讯.万科之类的: (1)首先说一下这两家公司的反馈情况: 腾讯:投递到反馈,(初次人事打电话沟通)大约1周,三次不 ...
- vue简介
vue的介绍 vue官网说:Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计. vue的优点 1.易用 ...
- 接口测试返回的json文件中字符串是乱序
问题描述 接口测试中post方式匹配返回信息时显示不匹配, 但是statuscode明明是200, 而且用postman /restclient等工具测出来也是没问题的. 根本原因 封装了这么个方法来 ...
- Python-分支循环- if elif for while
分支与循环 条件是分支与循环中最为核心的点,解决的问题场景是不同的问题有不同的处理逻辑.当满足单个或者多个条件或者不满足条件进入分支和循环,这里也就说明这个对相同问题处理执行逻辑依据具体参数动态变化, ...
- otter代码在IDEA远程DEBUG方法
众所周知,Otter的代码打包后,是通过Jetty启动的,Otter代码的启动脚本中自带了开启Jetty远程DEBUG的脚本,所以我们只需要在启动Otter Manager和Otter Node的时候 ...
- BZOJ_1097_[POI2007]旅游景点atr_状压DP
BZOJ_1097_[POI2007]旅游景点atr_状压DP 题面描述: FGD想从成都去上海旅游.在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣 的事情.经过这些城市的顺 ...
- Java IO--字符流--BufferedReader和BufferedWriter
从昨天开始没事情干时,决定梳理梳理Java IO流,因为觉得太混乱这个东西,妈的,咋就这么多类型,想累死谁啊,这里并不是埋怨创造者,而是气自己看着看着老跑偏,实在看不进去,太多了,想睡觉,所以现在决定 ...
- 数字类型——python3
今天我为各位小伙伴准备了python3中数字类型,希望能够帮助到你们! Python 数字数据类型用于存储数值. 数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间. 以下 ...
- RVM 安装 Ruby
RVM 是一个命令行工具,可以提供一个便捷的多版本 Ruby 环境的管理和切换. https://rvm.io/ 如果你打算学习 Ruby / Rails, RVM 是必不可少的工具之一. 这里所有的 ...
- validatebox相关验证
$(document).ready( function(){ $.extend($.fn.validatebox.defaults.rules, { minLength: { validator: f ...