正如标题所述,为什么挂起函数会在finally
中引发异常?
使用常规功能,finally
块将执行所有这些功能:
import kotlinx.coroutines.*
fun main() {
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
val job = GlobalScope.launch(handler) {
launch {
// the first child
try {
println("inside try")
delay(1000)
} finally {
println("Children are cancelled, but exception is not handled until all children terminate")
Thread.sleep(1000)
println("thread.sleep executed")
//foo()
println("The first child finished its non cancellable block")
}
}
launch {
// the second child
delay(10)
println("Second child throws an exception")
throw ArithmeticException()
}
}
Thread.sleep(1000000)
println("complete")
}
例如,在这里,当我执行Thread.sleep(1000)
时,它会打印:
“第一个孩子完成了不可取消的任务”
但是如果我将该行更改为delay(1000)
,则不会。
据我了解,在finally
块中,如果存在异常,则在执行整个块后引发该异常。
但是在这种情况下,delay
导致此异常尽早抛出。
另一方面,Thread.sleep
没有。
有人可以帮忙解释吗?
答案 0 :(得分:2)
Kotlin中的暂停功能与阻止功能的工作方式不同。
当您取消Job
时,在取消后的第一个暂停处,执行将被停止,即使您处于finally
块中,该操作也会甚至停止。如果您在Thread.sleep(1000)
块中使用delay(1000)
而不是finally
,则不会发生暂停,因为Thread.sleep()
被阻止,而不是暂停,因此您的整个finally
块都会被执行。
请注意,在挂起函数中使用阻塞函数是一种反模式,应避免!
要在不使用阻止功能的情况下实现此期望的行为,请按照here所述使用withContext(NonCancellable) {...}
。
您的示例代码应如下所示:
fun main() {
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
val job = GlobalScope.launch(handler) {
launch {
// the first child
try {
println("inside try")
delay(1000000)
} finally {
withContext(NonCancellable) {
println("Children are cancelled, but exception is not handled until all children terminate")
delay(1000) // This suspension cannot be cancelled
println("delay executed")
//foo()
println("The first child finished its non cancellable block")
}
}
}
launch {
// the second child
delay(10)
println("Second child throws an exception")
throw ArithmeticException()
}
}
Thread.sleep(1000000)
println("complete")
}
输出:
inside try
Second child throws an exception
Children are cancelled, but exception is not handled until all children terminate
delay executed
The first child finished its non cancellable block
Caught java.lang.ArithmeticException
答案 1 :(得分:0)
据我了解,在
finally
块中,如果存在异常,则在执行整个块后引发该异常。
这不是事实。如果finally
块引发异常,则会导致finally
块因该异常而突然终止。因此,在try
中引发的任何异常都将被丢弃。这正是您所遇到的情况:第一个子协程的finally
块在CancellationException
行上收到了delay(1000)
。 Thread.sleep(1000)
是一项不可取消的阻止性功能,因此它不会观察到取消。
您可能将其与以下事实混淆了:如果try
块引发异常,则首先执行完整的finally
块,然后引发异常。 finally
块需要正常完成才能完成。
所以我相信您在描述普通函数和可挂起的函数的行为方面没有任何区别。