为什么不要使用 async void?
程序员文章站
2022-07-11 08:53:47
问题 在使用 Abp 框架的后台作业时,当后台作业抛出异常,会导致整个程序崩溃。在 Abp 框架的底层执行后台作业的时候,有 语句块用来捕获后台任务执行时的异常,但是在这里没有生效。 原始代码如下: 调用接口时的效果: 原因 出现这种情况是因为任何异步方法返回 时,抛出的异常都会在 方法启动时,处于 ......
问题
在使用 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:
jerome laban's:
布鲁克石:
解决
可以使用 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?
-
跳转常规 -- 为什么不要使用404、500等http状态码作为业务代码响应
-
mybatis 为什么千万不要使用 where 1=1
-
谨慎使用javascript:void(0),为什么这样写不好
-
实例说明为什么不要行内使用javascript_javascript技巧
-
实例说明为什么不要行内使用javascript_javascript技巧
-
为什么在CSS中不要再使用@import_html/css_WEB-ITnose
-
JavaScript进阶系列—为什么不要使用 eval
-
为什么不要在MySQL中使用UTF-8编码方式
-
为什么不要使用 async void?