问题描述
运行此命令时:
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
处理程序.但事实并非如此.为什么会这样?
According to the documentation for CoroutineExceptionHandler, the eh
handler I provided should be executed. But it's not. Why is that?
推荐答案
我相信答案就在官方协同程序文档:
这也是为什么在这些示例中,CoroutineExceptionHandler始终安装到在GlobalScope中创建的协程的原因. 将异常处理程序安装到在主runBlocking范围内启动的协程中没有意义,因为尽管安装了该处理程序,但主协程将始终在其子进程完成异常时被取消 >.
This also a reason why, in these examples, CoroutineExceptionHandler is always installed to a coroutine that is created in GlobalScope. It does not make sense to install an exception handler to a coroutine that is launched in the scope of the main runBlocking, since the main coroutine is going to be always cancelled when its child completes with exception despite the installed handler.
(重点是我的)
这里描述的内容不仅适用于runBlocking
和GlobalScope
,而且还适用于任何非顶级协程生成器和自定义范围.
What's described here applies not just to runBlocking
and GlobalScope
, but any non-top-level coroutine builder and custom scope.
要进行说明(使用kotlinx.coroutines v1.0.0):
To illustrate (using 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
,但未执行.这类似于GlobalScope#launch
执行时的处理程序,但不是提供给runBlocking
内部任何launch
的处理程序.
Note that handler h1
is executed, but h2
isn't. This is analogous to the handler on GlobalScope#launch
executing, but not the handler provided to any launch
inside runBlocking
.
TLDR
提供给范围的非根协程的处理程序将被忽略.提供给根协程的处理程序将被执行.
正如Marko Topolnik在下面的注释中正确指出的那样,以上概括仅适用于launch
创建的协程.由async
或produce
创建的代码将始终忽略所有处理程序.
As correctly pointed out by Marko Topolnik in the comments below, the above generalization only applies to coroutines created by launch
. Those created by async
or produce
will always ignore all handlers.
这篇关于作为启动上下文提供时,CoroutineExceptionHandler不执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!