Кооперативная отмена: 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 — локальная установка не требуется.
Все уроки этого курса
- Кооперативная отмена: isActive и ensureActive
- withTimeout и withTimeoutOrNull
- Очистка с finally и NonCancellable
- Распространение отмены в иерархиях сопрограмм