Coroutines CancellationException 预期行为
Coroutines CancellationException expected behaviour
所以根据 Kotlin 对协程的介绍,在 Cancellation and Timeouts -> Run non-cancellable block 中我找到了以下解释:Any attempt to use a suspending function in the finally block [...] causes CancellationException
但是当 运行ning:
fun runPlayground() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
Log.d("XXX", "job: I'm sleeping $i ...")
delay(500L)
}
} finally {
doWorld() // logs "World" after 1s delay
delay(2000L)
Log.d("XXX", "job: I'm running finally")
}
}
delay(1300L) // delay a bit
Log.d("XXX", "main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
Log.d("XXX", "main: Now I can quit.")
}
记录结果:
D/XXX: job: I'm sleeping 0 ...
D/XXX: job: I'm sleeping 1 ...
D/XXX: job: I'm sleeping 2 ...
D/XXX: main: I'm tired of waiting!
D/XXX: main: Now I can quit.
最终块未执行可能是由于 运行ning 那里有暂停功能,但我希望在那里得到 CancellationException
,因为我 运行 代码来自:
try {
runPlayground()
} catch (e: CancellationException) {
Log.d("XXX", e.message)
}
异常是在协程内部处理的吗?
异常是从 doWorld()
抛出的,从那时起它逃脱协程块并被静静地吞噬,因为你没有安装任何未处理的 exception handler。它 运行 正在使用的调度程序不会因该异常而崩溃。
如果您研究上述 link 下的文档,您将了解到异常被默默吞噬只是因为它是 CancelationException
。任何其他异常至少会以与 Java 线程中出现未处理异常相同的方式显示,例如:
fun main() = runBlocking {
launch(Job()) {
throw Exception("I failed")
}.join()
println("runBlocking done")
}
这会打印
Exception in thread "main" java.lang.Exception: I failed
at org.mtopol.TestingKt$main.invokeSuspend(testing.kt:8)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at org.mtopol.TestingKt.main(testing.kt:6)
at org.mtopol.TestingKt.main(testing.kt)
runBlocking done
请特别注意 main
线程实际上并没有死:它继续打印行 runBlocking done
。 Kotlin 只是重用已安装的 currentThread().uncaughtExceptionHandler()
来记录协程失败。
当我去测试类似于上面的代码,但是安装了 CoroutineExceptionHandler
,我发现了一些问题:
- 忽略子协程中的异常处理程序。这似乎是设计使然,但没有记录在案。
runBlocking
似乎有一个错误,即使您为其安装了处理程序,它也不会 运行。
我为此创建了一个 issue。
所以根据 Kotlin 对协程的介绍,在 Cancellation and Timeouts -> Run non-cancellable block 中我找到了以下解释:Any attempt to use a suspending function in the finally block [...] causes CancellationException
但是当 运行ning:
fun runPlayground() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
Log.d("XXX", "job: I'm sleeping $i ...")
delay(500L)
}
} finally {
doWorld() // logs "World" after 1s delay
delay(2000L)
Log.d("XXX", "job: I'm running finally")
}
}
delay(1300L) // delay a bit
Log.d("XXX", "main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
Log.d("XXX", "main: Now I can quit.")
}
记录结果:
D/XXX: job: I'm sleeping 0 ...
D/XXX: job: I'm sleeping 1 ...
D/XXX: job: I'm sleeping 2 ...
D/XXX: main: I'm tired of waiting!
D/XXX: main: Now I can quit.
最终块未执行可能是由于 运行ning 那里有暂停功能,但我希望在那里得到 CancellationException
,因为我 运行 代码来自:
try {
runPlayground()
} catch (e: CancellationException) {
Log.d("XXX", e.message)
}
异常是在协程内部处理的吗?
异常是从 doWorld()
抛出的,从那时起它逃脱协程块并被静静地吞噬,因为你没有安装任何未处理的 exception handler。它 运行 正在使用的调度程序不会因该异常而崩溃。
如果您研究上述 link 下的文档,您将了解到异常被默默吞噬只是因为它是 CancelationException
。任何其他异常至少会以与 Java 线程中出现未处理异常相同的方式显示,例如:
fun main() = runBlocking {
launch(Job()) {
throw Exception("I failed")
}.join()
println("runBlocking done")
}
这会打印
Exception in thread "main" java.lang.Exception: I failed
at org.mtopol.TestingKt$main.invokeSuspend(testing.kt:8)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at org.mtopol.TestingKt.main(testing.kt:6)
at org.mtopol.TestingKt.main(testing.kt)
runBlocking done
请特别注意 main
线程实际上并没有死:它继续打印行 runBlocking done
。 Kotlin 只是重用已安装的 currentThread().uncaughtExceptionHandler()
来记录协程失败。
当我去测试类似于上面的代码,但是安装了 CoroutineExceptionHandler
,我发现了一些问题:
- 忽略子协程中的异常处理程序。这似乎是设计使然,但没有记录在案。
runBlocking
似乎有一个错误,即使您为其安装了处理程序,它也不会 运行。
我为此创建了一个 issue。