Как происходит прерывание корутин
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизм прерывания корутин в Kotlin
Прерывание корутин в Kotlin происходит через механизм отмены (cancellation), который является кооперативным - корутина должна регулярно проверять свой статус и корректно завершать работу при запросе отмены.
Основные принципы отмены
Job - ключевой интерфейс, представляющий корутину. Каждая корутина связана с Job, который управляет её жизненным циклом:
val job = CoroutineScope(Dispatchers.Default).launch {
// Работа корутины
}
Как инициировать прерывание
// 1. Отмена конкретной корутины
job.cancel()
// 2. Отмена всех корутин в скоупе
scope.cancel()
// 3. Отмена с причиной
job.cancel("Причина отмены", CancellationException())
Кооперативная проверка отмены
Корутина должна явно проверять статус отмены:
suspend fun doWork() {
val job = launch {
for (i in 1..1000) {
// Проверяем, не отменена ли корутина
ensureActive()
// Или альтернативная проверка
if (!isActive) {
throw CancellationException()
}
// Долгая операция
delay(100)
}
}
delay(500) // Ждём полсекунды
job.cancel() // Инициируем отмену
}
Автоматическая отмена в suspend функциях
Большинство стандартных suspend функций (delay(), yield(), withContext() и др.) автоматически проверяют отмену:
suspend fun processItems(items: List<Int>) {
items.forEach { item ->
delay(100) // delay() автоматически проверяет cancel()
// Если корутина отменена, delay() выбросит CancellationException
processItem(item)
}
}
Обработка исключений отмены
CancellationException - специальный тип исключения, который НЕ ловится обычными блоками try-catch:
val job = launch {
try {
repeat(1000) { i ->
println("Job: I'm sleeping $i ...")
delay(500)
}
} catch (e: CancellationException) {
// Обрабатываем отмену
println("Job: I was cancelled")
throw e // Важно: пробрасываем исключение дальше
} finally {
// Блок finally выполняется даже при отмене
println("Job: I'm running finally")
}
}
Отмена с таймаутом
// Автоматическая отмена после таймаута
withTimeout(1300) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500)
}
}
// Или с обработкой таймаута
val result = withTimeoutOrNull(1300) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500)
}
"Done" // Вернётся, если уложились в таймаут
}
Важные особенности:
- Кооперативность: Корутина не прерывается мгновенно. Она должна сотрудничать, проверяя флаг отмены.
- Нераспространяемость: CancellationException не прокидывается в родительскую корутину.
- Невозобновляемость: После отмены Job нельзя перезапустить.
- Иерархическая отмена: При отмене родительской Job отменяются все дочерние.
Пример комплексной отмены:
suspend fun complexOperation(): String = coroutineScope {
val job1 = async { fetchDataFromSource1() }
val job2 = async { fetchDataFromSource2() }
try {
val result1 = job1.await()
val result2 = job2.await()
combineResults(result1, result2)
} finally {
// Очистка ресурсов при отмене
if (isCancelled) {
cleanupResources()
}
}
}
// Вызов с таймаутом
try {
val result = withTimeout(5000) {
complexOperation()
}
} catch (e: TimeoutCancellationException) {
println("Operation timed out")
}
Рекомендации по работе с отменой:
- Всегда проверяйте isActive в долгих вычислительных циклах
- Используйте ensureActive() для удобной проверки
- Освобождайте ресурсы в блоке finally
- Избегайте блокирующих операций без проверки отмены
- Используйте yield() в CPU-интенсивных задачах для проверки отмены
Правильная обработка отмены делает корутины более надёжными и предотвращает утечки ресурсов при досрочном завершении операций.