问题

在使用 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

布鲁克石:https://www.cnblogs.com/brookshi/p/5240510.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?的更多相关文章

  1. 为什么不要使用 Async Void ?

    原文:为什么不要使用 Async Void ? 问题 在使用 Abp 框架的后台作业时,当后台作业抛出异常,会导致整个程序崩溃.在 Abp 框架的底层执行后台作业的时候,有 try/catch 语句块 ...

  2. 《C#并发编程经典实例》学习笔记—2.9 处理 async void 方法的异常

    问题 需要处理从 async void 方法传递出来的异常. 解决方案 书中建议尽量不写 async void 这样的方法,如果非写不可,建议在方法内部 try catch 所有的代码,即在方法内部处 ...

  3. 处理async void 方法中无法捕捉异常信息

    利用 NuGet库 Nito.AsyncEx 中的 AsyncContext类. 添加NuGet类库,使用AsyncContext AsyncContext.Run(Action action);

  4. [C#] async 的三大返回类型

    async 的三大返回类型 序 博主简单数了下自己发布过的异步文章,已经断断续续 8 篇了,这次我想以 async 的返回类型为例,单独谈谈. 异步方法具有三个可让开发人员选择的返回类型:Task&l ...

  5. async & await 的前世今生(Updated)

    async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...

  6. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  7. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  8. await and async

    Most people have already heard about the new “async” and “await” functionality coming in Visual Stud ...

  9. C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决

    返回目录 关于死锁的原因 理解该死锁的原因在于理解await 处理contexts的方式,默认的,当一个未完成的Task 被await的时候,当前的上下文将在该Task完成的时候重新获得并继续执行剩余 ...

随机推荐

  1. Python分词模块推荐:jieba中文分词

    一.结巴中文分词采用的算法 基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG)采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合对于未登录词,采 ...

  2. BZOJ_1367_[Baltic2004]sequence_结论题+可并堆

    BZOJ_1367_[Baltic2004]sequence_结论题+可并堆 Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 ...

  3. 关于throw、throws、try--catch的问题

    首先回顾概念 throws表示出现异常的一种可能性,并不一定会发生这些异常 throw则是抛出了异常,执行throw则一定抛出了某种异常 try--catch try语句用大括号{}指定了一段代码,该 ...

  4. 第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)

    本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks) ...

  5. C# - 如何让类型可以比较

    IComparable<T> .NET 里,IComparable<T>是用来作比较的最常用接口. 如果某个类型的实例需要与该类型的其它实例进行比较或者排序的话,那么该类型就可 ...

  6. PHP全栈学习笔记13

    php与ajax技术 web2.0的到来,ajax逐渐成为主流,什么是ajax,ajax的开发模式,优点,使用技术.(ajax概述,ajax使用的技术,需要注意的 问题,在PHP应用ajax技术的应用 ...

  7. Java语言编程 - Java第一个程序HelloWorld

    3.1 新建Java文件 首先新建一个文件夹,用于存放写的Java程序,例如我存放Java程序的位置为” D:\Files\code\java”. 在该文件夹中,右键新建一个文本文档 将文件名重命名为 ...

  8. uni-app实现顶部导航栏显示按钮+搜索框

    最近公司准备做app,最终决定使用uni-app框架开发,但是当把设计图给我的时候我心里有点没底,因为他的设计图顶部长成这个样子: 因为这个功能在小程序是根本无法实现的,可能受这个影响,我感觉好像实现 ...

  9. 微信小程序之滑动日历展示

    滑动日历效果 效果预览 实现要求:顶部固定悬浮的是获取未来一周的日期,分为上下两部分,上面部分显示星期,下面则显示具体日期.今天则显示今天,可点击头部具体日期,可向左向右滑动. 实现代码 顶部日历 页 ...

  10. mybatis一对一 和 一对多 嵌套查询

    实际项目中的,接口对外VO  会出现 一对一 和 一对多的情况,举例:小区 下面有 楼栋  ,楼栋 下面有 房屋    ,   房屋里面又房间 小区Vo  : districtVo { id: nam ...