运行此命令时:
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
处理程序。但事实并非如此。为什么会这样?
答案 0 :(得分:1)
您的kotlinx.coroutines
版本是什么?由于不推荐使用0.26.0独立launch
构建器,因此您应该使用GlobalScope.launch
。
我尝试了您的样本,并且在更改之后它起作用了。
答案 1 :(得分:1)
我相信答案就在official coroutines docs的这一部分:
如果协程遇到除CancellationException以外的异常,它将取消具有该异常的父对象。此行为不能被覆盖,并且用于为不依赖于CoroutineExceptionHandler实现的结构化并发提供稳定的协程层次结构。当父级的所有子级终止时,父级将处理原始异常。
这也是为什么在这些示例中,CoroutineExceptionHandler始终安装到在GlobalScope中创建的协程的原因。 将异常处理程序安装到在主runBlocking范围内启动的协程中没有意义,因为尽管已安装了处理程序,但主协程将始终在其子级异常完成时被取消。 >。
(重点是我的)
这里描述的内容不仅适用于runBlocking
和GlobalScope
,而且还适用于任何非顶级协程生成器和自定义范围。
说明:
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
创建的代码将始终忽略所有处理程序。