为什么不要使用 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 来监听异常,不过你是没办法从异常状态恢复的。

解决

可以使用 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的原因分析,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

(0)

相关推荐

  • spring boot使用自定义配置的线程池执行Async异步任务

    在前面的博客中,http://www.jb51.net/article/106718.htm 我们使用了spring boot的异步操作,当时,我们使用的是默认的线程池,但是,如果我们想根据项目来定制自己的线程池了,下面就来说说,如何定制线程池! 一.增加配置属性类 package com.chhliu.springboot.async.configuration; import org.springframework.boot.context.properties.ConfigurationP

  • Spring Boot利用@Async异步调用:ThreadPoolTaskScheduler线程池的优雅关闭详解

    前言 之前分享了一篇关于Spring Boot中使用@Async来实现异步任务和线程池控制的文章:<Spring Boot使用@Async实现异步调用:自定义线程池>.由于最近身边也发现了不少异步任务没有正确处理而导致的不少问题,所以在本文就接前面内容,继续说说线程池的优雅关闭,主要针对ThreadPoolTaskScheduler线程池. 问题现象 在上篇文章的例子Chapter4-1-3中,我们定义了一个线程池,然后利用@Async注解写了3个任务,并指定了这些任务执行使用的线程池.在上文

  • ASP.NET 谨用 async/await

    C# 5.0 引入 async/await 关键字,旨在简化异步编程模型,抛去语法糖就是 Net4.0 的 Task + 状态机.其实在处理异步编程使用 Task 还是挺简单的,不过既然推出了新的语法糖,难免会尝试一下,然而在使用中却没想象中那么单纯.以下针对ASP.NET 应用程序实际使用过程中的一些总结, 包括 异常捕获 . 死锁 . 应用程序崩溃 ,实际使用过程中一不注意就可能掉坑里了. 异常捕获 async 方法有三种返回类型: void.Task.Task async void 该方式

  • 深入理解js 中async 函数的含义和用法

    一.终极解决 异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题. 从最早的回调函数,到 Promise 对象,再到 Generator 函数,每次都有所改进,但又让人觉得不彻底.它们都有额外的复杂性,都需要理解抽象的底层运行机制. 异步I/O不就是读取一个文件吗,干嘛要搞得这么复杂?异步编程的最高境界,就是根本不用关心它是不是异步. async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案. 二.async 函数是什么? 一句话,

  • 使用async-validator编写Form组件的方法

    前端开发中,表单的校验一个很常见的功能,一些 ui 库例如ant.design与Element ui都实现了有校验功能的 Form 组件.async-validator是一个可以对数据进行异步校验的库,ant.design 与 Element ui 的 Form 组件都使用了 async-validator.本文就简单介绍一下 async-validator 的基本用法以及使用该库实现一个简单的有校验功能的 Form 组件. 1. async-validator 的基本用法 async-vali

  • Spring Boot 使用WebAsyncTask异步返回结果

    在Spring Boot中(Spring MVC)下请求默认都是同步的,一个请求过去到结束都是由一个线程负责的,很多时候为了能够提高吞吐量,需要将一些操作异步化,除了一些耗时的业务逻辑可以异步化,我们的查询接口也是可以做到异步执行. 一个请求到服务上,是用的web容器的线程接收的,比如线程http-nio-8084-exec-1 我们可以使用WebAsyncTask将这个请求分发给一个新的线程去执行,http-nio-8084-exec-1可以去接收其他请求的处理.一旦WebAsyncTask返

  • 关于Spring注解@Async引发其他注解失效的解决

    概述 在前面一篇文章中,介绍,在一个Bean中注入自己,如果有@Async和@Transaction,如果使用@Autowire注入自身,会报循环依赖,如果使用BeanFactoryAware注入自己,会使得@Transaction失效. 例如: @Service public class MyService implements BeanFactoryAware{ private MyService self; //事务注解无效 @Transactional public void notWo

  • 为什么不要使用 async void的原因分析

    问题 在使用 Abp 框架的后台作业时,当后台作业抛出异常,会导致整个程序崩溃.在 Abp 框架的底层执行后台作业的时候,有 try/catch 语句块用来捕获后台任务执行时的异常,但是在这里没有生效. 原始代码如下: public class TestAppService : ITestAppService { private readonly IBackgroundJobManager _backgroundJobManager; public TestAppService(IBackgro

  • SpringBoot使用Async注解失效原因分析及解决(spring异步回调)

    目录 Async注解失效原因分析及解决(spring异步回调) Spring中@Async 有时候在使用的过程中@Async注解会失效 解决方式一 解决方式二 springboot @Async 失效可能原因 Async注解失效原因分析及解决(spring异步回调) Spring中@Async 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已

  • spring 整合mybatis后用不上session缓存的原因分析

    因为一直用spring整合了mybatis,所以很少用到mybatis的session缓存. 习惯是本地缓存自己用map写或者引入第三方的本地缓存框架ehcache,Guava 所以提出来纠结下 实验下(spring整合mybatis略,网上一堆),先看看mybatis级别的session的缓存 放出打印sql语句 configuration.xml 加入 <settings> <!-- 打印查询语句 --> <setting name="logImpl"

  • Android Force Close 出现的异常原因分析及解决方法

    一.原因: forceclose,意为强行关闭,当前应用程序发生了冲突. NullPointExection(空指针),IndexOutOfBoundsException(下标越界),就连Android API使用的顺序错误也可能导致(比如setContentView()之前进行了findViewById()操作)等等一系列未捕获异常 二.如何避免 如何避免弹出Force Close窗口 ,可以实现Thread.UncaughtExceptionHandler接口的uncaughtExcepti

  • JS和jQuery使用submit方法无法提交表单的原因分析及解决办法

    昨天,在做一个表单异步提交内容的时候,遇到很奇怪的问题,submit()方法无法进行提交,每次提交都是把 当前给刷新了,网络抓包发现,每次都是 get方式去获取 当前页面,完全没有post 请求,想着以前 遇上这样的问题 都是因为 表单中 有 name 或者 id 这些命名跟submit 有冲突,但是检查了几次,始终没有发现 名字冲突,所以这个可能性被排除. 平常自己做触发按钮,基本不用a 标签,但是昨天不知道什么 问题,竟然用了 a 而且还给了 href 为空,由于这个a 的class 有多个

  • Android 中ListView setOnItemClickListener点击无效原因分析

    前言 最近在做项目的过程中,在使用listview的时候遇到了设置item监听事件的时候在没有回调onItemClick 方法的问题.我的情况是在item中有一个Button按钮.所以不会回调.上百度找到了解决办法有两种,如下: 1.在checkbox.button对应的view处加android:focusable="false" 复制代码 代码如下: android:clickable="false" android:focusableInTouchMode=&

  • ShareSDK造成App崩溃的一个BUG原因分析以及Fix方法

    近期研究了一下Game App做社交分享,最后选择了ShareSDK来集成,不仅是因为ShareSDK支持国内外主流社交平台,更重要的是ShareSDK提供了专门的 cocos2d-x集成方案,有专门的文档和代码Demo供开发者参考. 文档中提到了三种集成方式:纯Java方式.plugin-x方式以及Cocos2d-x专用组件方式,这里选择了ShareSDK Cocos2d-x专用组件(v2.3.7版本)的方式.按照文档中描述的步骤进行的相对顺利,在各个社交平台的appkey生效后,我们对dem

  • JavaWeb dbutils执行sql命令并遍历结果集时不能查到内容的原因分析

    JAVAWEB dbutils执行sql命令并遍历结果集时不能查到内容的原因及处理方法如下所示: 遍历结果集时只遍历bean对象才会只输出第一行那种内容(第一行是输出了UserEntity类实例化的对象),所以这里需要 re.getRepoTableName() 才能通过对象调用相对应的内容 这样一来,就可以取到值了 PS:JavaWeb之DBUtils详细介绍如下所示: 一.什么是DBUtils及作用 DBUtils是apache公司写的.DBUtils是java编程中的数据库操作实用工具,小

  • 解决Android加壳过程中mprotect调用失败的原因分析

    目录 问题原由 调用mprotect修改内存失败的现象 mprotect调用失败的原因分析 两种可行的解决方案 小结 问题原由 函数抽取壳是当前最为流行的DEX加壳方式之一,这种加壳方式的主要流程包含两个步骤:一.将DEX中需要保护的函数指令置空(即抽取函数体):二.在应用启动的过程中,HOOK 类的加载过程,比如ClassLinker::LoadMethod函数,然后及时回填指令. 笔者在实现抽取壳的过程中遇到了一个问题,即在步骤二回填指令之前,需要先调用mprotect将目标内存设置为“可写

  • Java中ThreadLocal 导致内存 OOM 的原因分析

    目录 原因分析 正确的使用方式 原因分析 ThreadLocal 导致内存 OOM 的原因是什么? ThreadLocal 底层通过 ThreadLocalMap 存储数据 源码如下:  当我们使用ThreadLocal.set()时,set的value与key(即业务自己定义的ThreadLocal类)会存储在ThreadLocalMap的Entry[]数组里 源码如下: 其中Entry是实现了一个弱引用WeakReference,Entry的key(即业务方定义的 ThreadLocal类)

随机推荐