Jul*_* A. 6 coroutine kotlin kotlinx.coroutines
当我运行这个:
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()
Run Code Online (Sandbox Code Playgroud)
这是输出:
[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)
Run Code Online (Sandbox Code Playgroud)
根据CoroutineExceptionHandler的文档,eh应该执行我提供的处理程序。但事实并非如此。这是为什么?
Jul*_* A. 14
我相信答案就在官方协程文档的这一节中:
如果协程遇到 CancellationException 以外的异常,它会用该异常取消其父进程。此行为不能被覆盖,并用于为不依赖于 CoroutineExceptionHandler 实现的结构化并发提供稳定的协程层次结构。当所有子进程终止时,原始异常由父进程处理。
这也是为什么在这些示例中 CoroutineExceptionHandler 始终安装到在 GlobalScope 中创建的协程的原因。将异常处理程序安装到在主 runBlocking 范围内启动的协程是没有意义的,因为尽管安装了 handler ,但当其子协程以异常完成时,主协程将始终被取消。
(强调我的)
此处描述的内容不仅适用于runBlockingand 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()
Run Code Online (Sandbox Code Playgroud)
输出:
[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
Run Code Online (Sandbox Code Playgroud)
请注意,处理程序h1已执行,但未执行h2。这类似于GlobalScope#launch执行时的处理程序,但不是提供给任何launchinside的处理程序runBlocking。
TLDR
提供给范围的非根协程的处理程序将被忽略。将执行提供给根协程的处理程序。
正如 Marko Topolnik 在下面的评论中正确指出的那样,上述概括仅适用于由launch. 由asyncor创建的那些produce将始终忽略所有处理程序。
| 归档时间: |
|
| 查看次数: |
2721 次 |
| 最近记录: |