0Pricing
Kotlin Academy · レッスン

協調的キャンセル:isActiveとensureActive

isActiveを確認しensureActiveを呼び出すことで、コルーチンをキャンセル可能にします。

「協調的キャンセル:isActiveとensureActive」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

協調的キャンセルが必要な理由

Kotlinのコルーチンは協調的キャンセルを使用します。キャンセルされたコルーチンは、キャンセル状態の確認によって協調した場合にのみ停止します。CPUを使い続けるループは、確認するまで停止しません。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        var i = 0
        while (true) { i++ } // never checks — never cancels!
    }
    delay(100)
    job.cancel()
    println("cancel sent but loop kept running until process ended")
}

isActiveの確認

isActiveはCoroutineScopeのプロパティで、キャンセルが要求されるとfalseを返します。ループ内で確認することで、コルーチンが協調的に動作します。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        var i = 0
        while (isActive) {
            i++
        }
        println("Loop exited after $i iterations")
    }
    delay(10)
    job.cancel()
    job.join()
}

ensureActive()

コルーチンがキャンセルされている場合、ensureActive()はCancellationExceptionをスローします。これはif (!isActive) throw CancellationException()の置き換えとしてそのまま使用できます。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            repeat(1_000_000) {
                ensureActive()  // throws if cancelled
                // heavy computation here
            }
        } catch (e: CancellationException) {
            println("Cancelled at iteration")
            throw e
        }
    }
    delay(5)
    job.cancel()
    job.join()
}

協調ポイントとしてのyield()

yield()はコルーチンを短時間中断し、スケジューラーがキャンセルを確認したり、ほかのコルーチンを実行したりできるようにします。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        repeat(10) { i ->
            yield()  // cooperative cancellation point + gives up scheduler
            println("Step $i")
        }
    }
    delay(2)
    job.cancel()
    job.join()
    println("Done")
}

isActiveとensureActiveの違い

条件分岐(たとえば、ループを正常に抜ける場合)にはisActiveを使用します。キャンセルを例外として直ちに伝播させたい場合はensureActive()を使用します。

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        repeat(100) { i ->
            if (!isActive) {
                println("Stopping at $i, cleaning up...")
                return@launch
            }
            // work
        }
    }.also { delay(5); it.cancel(); it.join() }
}

CancellationException は特別です

CancellationException は親へ伝播せず、通常のキャンセルを示します。握りつぶしてはいけません。必ず再スローするか、そのまま伝播させてください。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } catch (e: CancellationException) {
            println("Caught cancellation, rethrowing")
            throw e  // important!
        }
    }
    delay(50)
    job.cancel()
    job.join()
    println("Job state: ${job.isCancelled}")
}

CPUバウンド処理でのキャンセル

CPUバウンド処理では、自然なチェックポイントに ensureActive() または yield() を挿入し、速やかにキャンセルできるようにしてください。

import kotlinx.coroutines.*
suspend fun heavyCompute(n: Int): Long {
    var result = 0L
    for (i in 0..n) {
        ensureActive()
        result += i
    }
    return result
}
fun main() = runBlocking {
    val job = launch(Dispatchers.Default) {
        println(heavyCompute(1_000_000))
    }
    delay(10)
    job.cancel()
    job.join()
}

カスタムサスペンド関数での isActive

任意のサスペンド関数内で、coroutineContext.isActive または currentCoroutineContext().isActive を使って isActive にアクセスできます。

import kotlinx.coroutines.*
suspend fun checkable() {
    while (currentCoroutineContext().isActive) {
        delay(10)
        println("tick")
    }
}
fun main() = runBlocking {
    val job = launch { checkable() }
    delay(35)
    job.cancel()
    job.join()
}

リソースのクリーンアップを伴うキャンセル

協調的なキャンセルであっても、finally ブロックでリソースをクリーンアップできます。キャンセル例外はその中を伝播します。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            repeat(100) { i ->
                ensureActive()
                delay(20)
                println("Working $i")
            }
        } finally {
            println("Cleanup done")
        }
    }
    delay(50)
    job.cancel()
    job.join()
}

isActive を使ったポーリング

ポーリングループは isActive に最適です。コルーチンがキャンセルされるまでポーリングを続けてください。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        while (isActive) {
            println("Polling...")
            delay(100)
        }
        println("Poller stopped")
    }
    delay(350)
    job.cancel()
    job.join()
}

Thread.interrupted() との違い

Java のスレッドとは異なり、Kotlin のコルーチンは割り込みフラグに依存しません。キャンセルは例外とスコープの状態によってモデル化されているため、合成可能で構造化されています。

import kotlinx.coroutines.*
fun main() = runBlocking {
    // Kotlin way: cooperative via isActive / ensureActive
    launch {
        ensureActive()
        println("Kotlin coroutine: cancellation is explicit and structured")
    }
}

確認問題

コルーチンがキャンセルされたとき、即座に例外をスローする関数はどれですか。

まとめ

協調的なキャンセルには、明示的なチェックポイントが必要です。条件に応じて終了するには isActive、即座に例外をスローするには ensureActive()、サスペンドしてスレッドを解放するには yield() を使います。CancellationException を決して握りつぶさないでください。

よくある質問

「協調的キャンセル:isActiveとensureActive」レッスンは無料ですか?

はい。「協調的キャンセル:isActiveとensureActive」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「協調的キャンセル:isActiveとensureActive」で何を学びますか?

isActiveを確認しensureActiveを呼び出すことで、コルーチンをキャンセル可能にします。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「協調的キャンセル:isActiveとensureActive」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 協調的キャンセル:isActiveとensureActive
  2. withTimeoutとwithTimeoutOrNull
  3. finallyとNonCancellableによるクリーンアップ
  4. コルーチン階層におけるキャンセルの伝播
← Kotlin Academyに戻る