1. 異常な広がり
- ジョブのキャンセルと例外の伝播は双方向です (構造化された同時実行性)。ローカルのキャプチャと処理を行わずにコルーチンによって例外がスローされた場合、コルーチンは最初にすべてのサブコルーチンをキャンセルし、次にコルーチン自体をキャンセルします。例外が cancelException タイプの場合、伝播、SupervisorScope() またはvisorJob が使用されている場合、例外の種類に関係なく、上向きの伝播は終了します。そうでない場合は、ルート コルーチンに渡され、構造全体のすべてのコルーチンがキャンセルされます。 。
- CancelException 型の例外は、それらをスローするコルーチンによって処理されます。SupervisorJob 型の例外は、この層の下位サブコルーチン (例外をスローするコルーチンまたは親コルーチン) によって処理されます。ただし、これらは両方とも SupervisorJob のサブコルーチンです。それ以外の場合、例外はルート コルーチンによって処理されます。
2.通信を中断する
例外を上方向に伝播させたくない場合、または兄弟コルーチンに影響を与えたくない場合に使用します (下方向への伝播はまだ存在します)。
2.1 スーパーバイザージョブ
内部例外は親コルーチンと兄弟コルーチンをキャンセルしたり、それらの影響を受けたりしませんが、内部サブコルーチン内で発生した例外は相互に影響を与えます。例外の処理に失敗すると、プログラムがクラッシュする可能性があります。
//避免在只有一个子协程的时候传参使用(孙协程仍会相互取消,作用相当于普通job)
launch(SupervisorJob()) { //唯一子协程
launch {
println("子协程1")
throw Exception() //会相互取消
}
launch {
println("子协程2") //不会打印
}
}
//正确写法
val job = SupervisorJob()
launch(job) {
println("子协程1")
throw Exception() //不会相互取消
}
launch(job) {
println("子协程2") //会打印
}
2.2 スーパーバイザースコープ()
supervisorScope{
launch{
println("子协程1")
throw Exception()
}
launch{
println("子协程2") //会打印
}
}
三、キャンセル例外
例外が cancelException とそのサブクラスの場合、例外は上方に渡されず、現在のコルーチンとそのサブクラスのみがキャンセルされます。
//分别开启1和2两个协程,抛出异常会结束1和它的子协程3,但不会影响2
object MyException : CancellationException()
suspend fun main(): Unit = coroutineScope {
launch { //1
launch { //3
delay(2000)
println("1") //不会打印
}
throw MyException
}
launch { //2
delay(2000)
println("2") //会打印
}
}
4. 例外キャッチ
打ち上げ | コード ブロックでスローされた例外は直接スローされます。 |
非同期 | コード ブロックでスローされた例外は、最後の消費、つまり await() の呼び出し後にのみスローされます。サブコルーチン内の例外は await() の影響を受けず、キャッチされなかった例外は処理のためにルート コルーチンに渡されます。 。したがって、各 await() を個別にキャプチャすることは、他の並行タスクに影響を与えるクラッシュを回避するためであり、すべての async をキャプチャすることは、サブコルーチン例外が上方に渡されてプログラムがクラッシュすることを防ぐことになります (coroutineScope( のレイヤーを置くこともできます)。 ) その外側では例外がスローされ、上方には渡されません).catch)、または CoroutineExceptionHandler を使用します。 |
4.1 トライキャッチ
キャプチャ コルーチン ビルダーが無効です。コルーチン コード ブロックでスローされた例外をキャプチャするには: コルーチン コード ブロックでスローされた例外がキャプチャおよび処理されない場合、例外はキャプチャされ、BaseContinuationImpl.resumeWith() の Result オブジェクトにカプセル化されます。最後に、例外ハンドラーに渡された場合は、再度スローされないため、キャッチする例外はありません、つまり、ビルダーは例外をスローしません。
中断された関数で子スレッドの例外をキャプチャできます。子スレッドのキャプチャ試行は無効であり、現在のスレッドのスタック情報のみをキャプチャできます。子スレッドを有効にする一時停止関数の例外は、一時停止関数の基になるコードが reusmeWithExceptoon() を使用して子スレッドから現在のスレッドに例外を運び、スローするため、コルーチンでキャプチャできます。投げると直接捕まり、永続的なハングの原因となります。
4.2 コルーチン例外ハンドラー
- 例外はルート コルーチンまで伝播されます。ルート コルーチンが応答しない場合 (SupervisorScope() またはvisorJob)、直接のサブコルーチンは処理のコンテキスト内で CoroutineExceptionHandler を探します。そうでない場合は、UncaughtExceptionHandler が返されます。使用されるため、他のサブコルーチンの CoroutineExceptionHandler は機能しません。
- 使用条件: コルーチン スコープのコンテキスト内に存在するか、ルート コルーチンの直接の子コルーチンに存在します。
- 現在のスコープ内の例外のみを処理できます。たとえば、別のスコープ オブジェクトによって開かれたコルーチンがコード ブロック内で呼び出された場合、発生した例外はスコープ オブジェクトのコンテキストで構成された CoroutineExceptionHandler によって処理される必要があります。
//不会阻止异常传递,但可以定义在发生异常时的处理行为(默认情况它会打印异常堆栈)
fun main(): Unit = runBlocking {
val parentExceptionHandler = CoroutineExceptionHandler { _, throwable -> println("调用根协程异常处理器:${throwable.message}") }
val childExceptionHandler = CoroutineExceptionHandler { _, throwable -> println("调用子协程异常处理器:${throwable.message}") }
CoroutineScope().launch(parentExceptionHandler) {
launch(childExceptionHandler) {
throw Exception("子协程使用的是Job") //打印:调用根协程异常处理器
}
}
CoroutineScope().launch(parentExceptionHandler) {
launch(SupervisorJob() + childExceptionHandler) {
throw Exception("子协程使用的是SupervisorJob") //打印:调用子协程异常处理器
}
}
delay(1000)
}