「[:en]CancellationException[:ja]キャンセル例外[:]」タグアーカイブ

Kotlin 1.3: コルーチンでの例外の扱い方 (2/2)


Kotlin 1.3: コルーチンでの例外の扱い方 (1/2) からの続きです。

続きを読む Kotlin 1.3: コルーチンでの例外の扱い方 (2/2)

Kotlin 1.3: コルーチンでの例外の扱い方 (1/2)


Kotlin 1.3 で使えるようになる コルーチンでの例外の扱いについてドキュメントを読んでまとめました。

ここでは例外処理と例外発生時のキャンセル処理について記述しています。 キャンセルされたコルーチンは CancellationException を停止位置でスローします。 そして、コルーチンの機構によりそれは無視されます。 しかし、 例外がキャンセルされたり、 同じコルーチンの複数の子が例外をスローした場合はどうなるでしょうか。

検証環境

  • OpenJDK 10.0.2
  • Kotlin 1.3.0-rc-146
  • org.jetbrains.kotlinx:kotlinx-coroutines-core:0.30.2

例外の伝播

コルーチンビルダには2つの種類があります。 例外を自動的に伝播するもの (launch, actor) と ユーザに例外処理を委ねるもの (async, produce)。 前者は Java の Thread.uncaughtExceptionHandler のように、 例外を未処理のものとして扱います。 後者はユーザが最終的な例外をどのように処理するかに依存しており、 例えば await, receive があります。

GlobalScope を使った単純な例で実験してみます。

出力は次のようになります。

launch はその場で例外が発生しており、 await は処理実行時に例外を発生させています。

CoroutineExceptionHandler

もしユーザが例外の全てをコンソールに表示したくない場合はどうでしょうか。 CoroutineExceptionHandler コンテキスト が、 コルーチンの一般的な catch ブロックに使用することができ、 独自のログ出力や例外処理を行う際に使用できます。 これは Thread.uncaughtExceptionHandler に似ています。

JVM では ServiceLoader を経由して CoroutineExceptionHander を登録することで、 全てのコルーチンに対して グローバル例外ハンドラ を再定義することができます。 グローバル例外ハンドラは、 他に特別なハンドラが登録されていない場合の Thread.defaultUncaughtExceptionHandler に似ています。 Android では uncaughtExceptionPreHandler がグローバルコルーチン例外ハンドラとして登録されています。

CoroutineExceptionHandler は ユーザが予期していなかった例外が発生した場合にのみ実行されます。 そのため async のビルダなどに登録しても効果がありません。

このコードのアウトプットは次のようになります。

launch でも、 async でも CoroutineExceptionHandler を使っていますが、 async の方では意味をなしていません。 コードが実行されていないためです。

次のようにコードを変えてみます。

出力が変わってきます。

出力は変わったものの、 async で登録した CoroutineExceptionHandler は実行されていません。

async の中で launch を実行しても、 スタックトレースは変わるものの、同じ結果になります。

Kotlin 1.3: コルーチンでの例外の扱い方 (2/2) に続きます。