Что такое прерывание у корутин?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое прерывание у корутин?
Прерывание (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().
Прерывание корутин — мощный инструмент для управления жизненным циклом асинхронных операций, но его эффективность напрямую зависит от соблюдения принципов кооперативности. Правильная реализация обеспечивает отзывчивость приложений и эффективное использование ресурсов.