0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlin コルーチンコンテキスト(CoroutineContext)徹底解説

Posted at

はじめに

コルーチンは軽量スレッドのように見えますが、実際には
コンテキスト(CoroutineContext)」という仕組みで管理されています。

コルーチンは「どのスレッドで」「どんなジョブとして」「どんなルールで」動くのか
→ それをすべて決めるのが CoroutineContext です。


1. コルーチンコンテキストとは?

CoroutineContext は、コルーチンに関する「環境設定」をまとめたものです。
次のような要素を保持しています。

要素 役割
Job Job キャンセルや完了状態の管理
Dispatcher CoroutineDispatcher 実行スレッド(どこで動くか)
CoroutineName CoroutineName デバッグ用の名前識別子
CoroutineExceptionHandler CoroutineExceptionHandler 例外ハンドリング

2. コンテキストの構成イメージ

これらの要素が組み合わさって「コルーチンがどのように動作するか」を決定します。


3. コンテキストの指定方法

launch / async などの起動時に、context を指定できます。

launch(Dispatchers.IO + CoroutineName("FileReader")) {
    println("Running on ${Thread.currentThread().name}")
}

出力例:

Running on DefaultDispatcher-worker-1

+ 演算子で複数の要素を合成できます。
例:Dispatchers.IO + Job() + CoroutineName("MyTask")


4. Dispatcher の種類(どのスレッドで動くか)

Dispatcher 説明 用途例
Dispatchers.Default CPU負荷の高い処理用スレッドプール 並列計算、ソート
Dispatchers.IO I/O最適化スレッドプール ネットワーク、DB、ファイル処理
Dispatchers.Main メインスレッド(UIスレッド) Android の UI 更新
Dispatchers.Unconfined 呼び出し元スレッドで開始(再開時は未定) 特殊ケース(テストなど)

例:

launch(Dispatchers.Default) { println("CPU heavy task") }
launch(Dispatchers.IO) { println("Network task") }
launch(Dispatchers.Main) { println("Update UI") }

5. Job:キャンセルと階層構造

コルーチンのキャンセル状態や完了状態を管理します。

val parent = Job()
val context = Dispatchers.Default + parent

CoroutineScope(context).launch {
    println("Running...")
}

キャンセル

parent.cancel() // 子コルーチンもすべてキャンセルされる

これが「構造化並行性(Structured Concurrency)」の核です。


6. CoroutineName:デバッグのための識別名

launch(CoroutineName("Uploader")) {
    println("Coroutine name: ${coroutineContext[CoroutineName]}")
}

出力:

Coroutine name: CoroutineName(Uploader)

ログ分析・デバッグ時に便利。
Thread 名ではなく、論理的なタスク名として識別できます。


7. CoroutineExceptionHandler:例外処理を一元化

val handler = CoroutineExceptionHandler { _, exception ->
    println("Caught: ${exception.message}")
}

CoroutineScope(Dispatchers.Default + handler).launch {
    throw RuntimeException("Something went wrong!")
}

出力:

Caught: Something went wrong!

ポイント:

  • 子コルーチンで未処理の例外が発生した場合、ハンドラがキャッチする。
  • async は例外を await() 時に再スローする(自動伝播しない点に注意)

8. コンテキストの取得と利用

現在のコルーチンのコンテキストを取得できます。

launch(Dispatchers.IO + CoroutineName("Downloader")) {
    println(coroutineContext[CoroutineDispatcher])  // Dispatcher情報
    println(coroutineContext[CoroutineName])        // CoroutineName
}

coroutineContextすべての suspend 関数内で利用可能 です。


9. withContext とコンテキストの切り替え

suspend fun loadData() {
    withContext(Dispatchers.IO) {
        println("I/O thread: ${Thread.currentThread().name}")
    }

    withContext(Dispatchers.Default) {
        println("CPU thread: ${Thread.currentThread().name}")
    }
}

出力:

I/O thread: DefaultDispatcher-worker-2
CPU thread: DefaultDispatcher-worker-4

withContext は「コンテキストを切り替える」機能。
スレッドや Dispatcher を安全に変更できます。


10. コンテキストの継承と上書き

親スコープのコンテキストは、子コルーチンにも自動で引き継がれます。
ただし、指定した要素は上書きされます。

val scope = CoroutineScope(Dispatchers.Default + CoroutineName("Parent"))

scope.launch(CoroutineName("Child")) {
    println(coroutineContext[CoroutineName]) // Child に上書きされる
}

11. 図で理解するコンテキストの構造

  • Scope:コンテキストを持つ「コルーチンの枠」
  • Context:構成要素を保持(Dispatcher, Job, Name, Handler)
  • Coroutine:Scope + Context のもとで動く実行単位

まとめ

要素 主な役割
Job Job コルーチンのキャンセル・完了管理
Dispatcher CoroutineDispatcher 実行スレッドの決定
CoroutineName CoroutineName 論理的な識別名(デバッグ用)
CoroutineExceptionHandler CoroutineExceptionHandler 例外処理のカスタマイズ
  • コルーチンは「コンテキスト」で全てが決まる

  • + で合成し、柔軟に制御できる

  • coroutineContext は suspend 関数内でアクセス可能

  • 親子のコンテキストは自動的に継承・伝播される

  • コルーチンコンテキストは「どのスレッド・どのルール・どの識別名で動作するか」を決める

  • Dispatcher でスレッドを決め、Job でライフサイクルを制御し ExceptionHandler で安全性を担保する

  • すべてのコルーチンは「Context」の上に存在する

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?