← Назад к вопросам

Что такое прерывание у корутин?

3.0 Senior🔥 161 комментариев
#Kotlin основы#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое прерывание у корутин?

Прерывание (cancellation) у корутин в Kotlin — это кооперативный механизм, позволяющий корректно остановить выполнение корутины до её естественного завершения. В отличие от потоков, где можно принудительно прервать выполнение (метод Thread.stop()), корутины требуют кооперации: код внутри корутины должен периодически проверять статус прерывания и реагировать на него. Это обеспечивает безопасность — предотвращает внезапную остановку в середине критических операций, что могло бы привести к утечкам ресурсов или повреждению данных.

Ключевые аспекты прерывания корутин

1. Инициация прерывания

Прерывание инициируется вызовом метода cancel() у Job или отменой всей области видимости (CoroutineScope). Это устанавливает состояние корутины в "отменённое" (cancelling), но не останавливает её немедленно:

val job = launch {
    // Длительная операция
}
job.cancel() // Запрос на прерывание

2. Кооперативная проверка прерывания

Корутина должна явно проверять статус прерывания через:

  • Приостановочные функции (suspend functions): Большинство стандартных функций (например, delay(), yield(), withContext()) автоматически проверяют прерывание и выбрасывают CancellationException при отмене.
  • Явную проверку: Функции ensureActive() или isActive:
launch {
    while (isActive) { // Проверка состояния
        // Работа
    }
    // Выход при isActive == false
}

// Или с ensureActive()
launch {
    for (i in 1..1000) {
        ensureActive() // Выбросит CancellationException при отмене
        // Работа
    }
}

3. Обработка исключения CancellationException

При прерывании выбрасывается CancellationException — специальное исключение, которое по умолчанию не логируется и не требует обязательной обработки. Однако его можно перехватить для очистки ресурсов, но после этого необходимо повторно выбросить:

launch {
    try {
        delay(5000)
    } catch (e: CancellationException) {
        // Очистка ресурсов
        println("Корутина отменена")
        throw e // Обязательно повторно выбросить!
    }
}

4. Некорректная обработка прерывания

Если корутина не проверяет прерывание (например, выполняет CPU-интенсивный цикл без вызовов suspend-функций), она может продолжить работу даже после вызова cancel():

launch {
    var i = 0
    while (i < 1_000_000) {
        // Плотный цикл без проверки isActive — прерывание игнорируется!
        i++
    }
}

Решение — добавить проверку ensureActive() или yield() внутри цикла.

5. Неотменяемые блоки с withContext(NonCancellable)

Иногда нужно выполнить код даже после прерывания (например, закрыть файл или откатить транзакцию). Для этого используется NonCancellable:

launch {
    try {
        // Длительная операция
    } finally {
        withContext(NonCancellable) {
            delay(1000) // Можно вызывать suspend-функции
            println("Гарантированная очистка")
        }
    }
}

6. Время жизни прерывания

  • Сквозное прерывание: Если отменяется родительская корутина, все её дочерние корутины также отменяются.
  • Исключения из правила: SupervisorJob позволяет дочерним корутинам завершаться независимо, но ручной вызов cancel() у родителя всё равно отменит всех детей.

Практические рекомендации

  • Всегда делайте код кооперативным: Используйте стандартные suspend-функции или явные проверки isActive/ensureActive() в длительных операциях.
  • Очищайте ресурсы в finally или withContext(NonCancellable): Это предотвращает утечки.
  • Избегайте блокирующего кода: Вместо Thread.sleep() используйте delay(), которая чувствительна к прерыванию.
  • Учитывайте влияние на асинхронные операции: Например, при отмене корутины, выполняющей сетевой запрос, используйте cancel() на уровне сетевой библиотеки или withTimeout().

Прерывание корутин — мощный инструмент для управления жизненным циклом асинхронных операций, но его эффективность напрямую зависит от соблюдения принципов кооперативности. Правильная реализация обеспечивает отзывчивость приложений и эффективное использование ресурсов.