kotlin - 作为启动上下文提供时未执行 CoroutineExceptionHandler
问题描述
当我运行这个:
fun f() = runBlocking {
val eh = CoroutineExceptionHandler { _, e -> trace("exception handler: $e") }
val j1 = launch(eh) {
trace("launched")
delay(1000)
throw RuntimeException("error!")
}
trace("joining")
j1.join()
trace("after join")
}
f()
这是输出:
[main @coroutine#1]: joining
[main @coroutine#2]: launched
java.lang.RuntimeException: error!
at ExceptionHandling$f9$1$j1$1.invokeSuspend(ExceptionHandling.kts:164)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)
根据CoroutineExceptionHandler的文档,我提供的eh
处理程序应该被执行。但事实并非如此。这是为什么?
解决方案
我相信答案就在官方协程文档的这一部分:
如果协程遇到除 CancellationException 以外的异常,它会取消其父级并出现该异常。此行为不能被覆盖,并用于为不依赖于 CoroutineExceptionHandler 实现的结构化并发提供稳定的协程层次结构。当所有子级终止时,原始异常由父级处理。
这也是为什么在这些示例中,总是将 CoroutineExceptionHandler 安装到在 GlobalScope 中创建的协程中的原因。将异常处理程序安装到在主 runBlocking 范围内启动的协程是没有意义的,因为尽管安装了处理程序,但当其子协程以异常完成时,主协程总是会被取消。
(强调我的)
此处描述的内容不仅适用于runBlocking
and GlobalScope
,还适用于任何非顶级协程构建器和自定义范围。
为了说明(使用 kotlinx.coroutines v1.0.0):
fun f() = runBlocking {
val h1 = CoroutineExceptionHandler { _, e ->
trace("handler 1 e: $e")
}
val h2 = CoroutineExceptionHandler { _, e ->
trace("handler 2 e: $e")
}
val cs = CoroutineScope(newSingleThreadContext("t1"))
trace("launching j1")
val j1 = cs.launch(h1) {
delay(1000)
trace("launching j2")
val j2 = launch(h2) {
delay(500)
trace("throwing exception")
throw RuntimeException("error!")
}
j2.join()
}
trace("joining j1")
j1.join()
trace("exiting f")
}
f()
输出:
[main @coroutine#1]: launching j1
[main @coroutine#1]: joining j1
[t1 @coroutine#2]: launching j2
[t1 @coroutine#3]: throwing exception
[t1 @coroutine#2]: handler 1 e: java.lang.RuntimeException: error!
[main @coroutine#1]: exiting f
请注意,处理程序h1
已执行,但未执行h2
。这类似于GlobalScope#launch
执行时的处理程序,但不是提供给任何launch
内部的处理程序runBlocking
。
TLDR
提供给作用域的非根协程的处理程序将被忽略。将执行提供给根协程的处理程序。
正如 Marko Topolnik 在下面的评论中正确指出的那样,上述概括仅适用于由launch
. 那些由创建async
或produce
将始终忽略所有处理程序。
推荐阅读
- excel - Excel:计算范围内匹配条件的唯一值数量的公式
- r - 应用pivot_wider后如何将YAML标头中设置的字符值参数读取为列名?
- ruby-on-rails - Ruby Rails 在 ERB 搜索最佳实践中创建变量
- python - 接收阻塞异步的套接字?
- node.js - 如何减少项目中的nodejs大小?
- json - 单个 GraphQL 查询中的多个源 (Gatsby)
- react-native - 在 ScrollView 中使用 TouchableOpacity
- javascript - 反转超过 4 个字符的单词
- scala - 我可以使用函数组合来避免scala中的“临时列表”吗?
- c# - 将文件存储到字节数组