CancellationException は、コルーチンがコルーチンをキャンセルするために特別に使用され、他の例外とは処理が異なります。
- コルーチンは内部で CancellationException を使用して操作をキャンセルしますが、この例外は無視されます。
- 子コルーチンがキャンセルされても、親コルーチンはキャンセルされません。(その他の例外であれば親コルーチンも終了!)
- コルーチンが CancellationException 以外の例外に遭遇した場合、その例外を使用して親コルーチンをキャンセルします。親コルーチンと子コルーチンの両方が終了すると、例外は親コルーチンによって処理されます。
次の例では、文 println("Exception capture ${exception.message}") が最後に実行されます。これは、上記の 3 番目のポイントで、親コルーチンがすべての子コルーチンで例外を処理すると述べているためです。すべて実行されます。
step1: 2 番目の子コルーチンが例外をスローした場合、親コルーチンに CancellationException をスローして、親コルーチンをキャンセルします。
ステップ 2: 親コルーチンが最初のサブコルーチンに例外を渡し、最初のサブコルーチンが例外をキャッチして最後に実行し、リソースをクリーンアップします。(ここでリソースをクリーンアップする場合は withContext(NonCancellable) パッケージを使用する必要があります。これにより、リソースのクリーンアップ作業が他の例外によって妨げられないためです)
Step3: サブコルーチンがすべて実行されると、ここで最初のサブコルーチンが最終的に実行され、ハンドラーを使用して 2 番目のサブコルーチンによってスローされた例外を処理します。
@Test
fun coroutineExceptionHandlerTest2() = runBlocking<Unit> {
var handler = CoroutineExceptionHandler{ _ , exception ->
println("异常捕获 ${exception.message}")
}
var job1 = GlobalScope.launch(handler) {
launch {
try {
println("第一个子协程进入")
delay(Long.MAX_VALUE)
} catch (e : java.lang.Exception) {
println("第一个子协程捕获到异常,${e.message}")
} finally {
withContext(NonCancellable) {
println("第一个子协程准备清理资源")
delay(5000)
println("第一个子协程准备清理资源完毕")
}
}
}
launch {
println("第二个子协程进入")
delay(1000)
throw ArithmeticException("抛出一个算数异常!!!!!!!!!")
}
}
job1.join()
}
第一个子协程进入
第二个子协程进入
第一个子协程捕获到异常,Parent job is Cancelling
第一个子协程准备清理资源
第一个子协程准备清理资源完毕
异常捕获 抛出一个算数异常!!!!!!!!!