協調的キャンセル: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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 協調的キャンセル:isActiveとensureActive
- withTimeoutとwithTimeoutOrNull
- finallyとNonCancellableによるクリーンアップ
- コルーチン階層におけるキャンセルの伝播