0Pricing
Kotlin Academy · Урок

Кооперативная отмена: isActive и ensureActive

Делайте сопрограммы отменяемыми, проверяя isActive и вызывая ensureActive.

«Кооперативная отмена: isActive и ensureActive» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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 в пользовательской приостанавливаемой функции

Получить доступ к isActive внутри любой приостанавливаемой функции можно через coroutineContext.isActive или с помощью currentCoroutineContext().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/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Кооперативная отмена: isActive и ensureActive»?

Делайте сопрограммы отменяемыми, проверяя isActive и вызывая ensureActive. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Кооперативная отмена: isActive и ensureActive»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Кооперативная отмена: isActive и ensureActive
  2. withTimeout и withTimeoutOrNull
  3. Очистка с finally и NonCancellable
  4. Распространение отмены в иерархиях сопрограмм
← Назад к Kotlin Academy