Очистка с finally и NonCancellable
Освобождайте ресурсы в отменённых сопрограммах с помощью finally и withContext(NonCancellable).
«Очистка с finally и NonCancellable» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Отмена и завершающие блоки
При отмене корутины точки приостановки выбрасывают CancellationException. Код в блоках finally выполняется всегда, однако сам finally выполняется в уже отменённом контексте корутины.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
delay(1000)
} finally {
println("finally: cleaning up")
}
}
delay(50)
job.cancel()
job.join()
}Проблема: приостановка в завершающем блоке
Если попытаться вызвать delay или выполнить другую приостановку внутри finally отменённой корутины, функция немедленно выбросит исключение, поскольку контекст уже отменён.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
delay(1000)
} finally {
// delay(100) // throws CancellationException here!
println("Sync cleanup OK; async cleanup needs NonCancellable")
}
}
delay(50); job.cancel(); job.join()
}withContext(NonCancellable)
Оборачивайте вызовы приостановки в finally с помощью withContext(NonCancellable), чтобы выполнять их в контексте, который нельзя отменить, даже после отмены родительской корутины.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
delay(1000)
} finally {
withContext(NonCancellable) {
delay(100) // OK: runs despite cancellation
println("Async cleanup complete")
}
}
}
delay(50); job.cancel(); job.join()
}Когда использовать NonCancellable
Используйте NonCancellable для закрытия подключений к базе данных, записи журналов, отправки событий аналитики и любой асинхронной очистки, которая должна завершиться независимо от отмены.
import kotlinx.coroutines.*
suspend fun closeConnection() {
println("Closing DB connection...")
delay(50) // simulate async close
println("Connection closed")
}
fun main() = runBlocking {
val job = launch {
try { delay(1000) }
finally { withContext(NonCancellable) { closeConnection() } }
}
delay(50); job.cancel(); job.join()
}NonCancellable — это не область действия
NonCancellable — это Job, который можно передать в withContext. Не используйте его напрямую как область действия корутины — применяйте его только для очистки в finally.
import kotlinx.coroutines.*
fun main() = runBlocking {
// WRONG usage:
// launch(NonCancellable) { ... } // launches a coroutine that cannot be cancelled
// RIGHT usage:
launch {
try { delay(500) }
finally {
withContext(NonCancellable) {
println("Safe cleanup")
}
}
}.also { delay(50); it.cancel(); it.join() }
}Очистка нескольких ресурсов
Объединяйте несколько операций очистки внутри одного блока withContext(NonCancellable).
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
try {
delay(1000)
} finally {
withContext(NonCancellable) {
delay(20); println("Step 1: flush cache")
delay(20); println("Step 2: close socket")
delay(20); println("Step 3: log audit")
}
}
}.also { delay(50); it.cancel(); it.join() }
}Шаблон обработки ошибок и очистки
Сочетайте обработку бизнес-ошибок через try-catch с очисткой в finally — это стандартный структурированный шаблон.
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
try {
delay(100)
throw RuntimeException("business error")
} catch (e: RuntimeException) {
println("Handling: ${e.message}")
} finally {
withContext(NonCancellable) {
println("Always cleanup")
}
}
}.join()
}Порядок очистки
Блоки finally выполняются в обратном порядке вложенности. Внешний блок try-finally выполняется после внутренних, как и в обычном коде Java/Kotlin.
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
try {
try {
delay(1000)
} finally { println("Inner cleanup") }
} finally { println("Outer cleanup") }
}.also { delay(50); it.cancel(); it.join() }
}Автоматическое закрытие ресурсов AutoCloseable
Для ресурсов AutoCloseable используйте расширение Kotlin use { }: оно автоматически вызывает close(), в том числе при исключениях или отмене.
import kotlinx.coroutines.*
class Connection : AutoCloseable {
init { println("Opened") }
override fun close() { println("Closed") }
suspend fun query(): String { delay(50); return "result" }
}
fun main() = runBlocking {
val result = Connection().use { it.query() }
println(result)
}Запись причины отмены в журнал
Получайте сообщение об отмене из CancellationException в блоках обработки или завершения, чтобы записать причину остановки корутины в журнал.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
delay(1000)
} catch (e: CancellationException) {
println("Cancelled: ${e.message}")
throw e
}
}
delay(50)
job.cancel("user navigated away")
job.join()
}Очистка CoroutineScope с помощью invokeOnCompletion
Job.invokeOnCompletion регистрирует обратный вызов, который выполняется после завершения задания по любой причине, — это альтернатива finally для очистки на уровне родительской корутины.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
delay(1000)
}
job.invokeOnCompletion { cause ->
if (cause != null) println("Ended with: ${cause::class.simpleName}")
else println("Completed normally")
}
delay(50)
job.cancel()
job.join()
}Быстрая проверка
Для чего нужен withContext(NonCancellable) в завершающем блоке finally?
Итоги
finally всегда выполняется при отмене. Оборачивайте вызовы приостановки внутри finally в withContext(NonCancellable), чтобы они завершались несмотря на отмену. Для ресурсов AutoCloseable используйте use { }.
Изучай Kotlin с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 51
- Уроки
- 203
Часто задаваемые вопросы
Урок «Очистка с finally и NonCancellable» бесплатный?
Да — полный текст урока «Очистка с finally и NonCancellable» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «Очистка с finally и NonCancellable»?
Освобождайте ресурсы в отменённых сопрограммах с помощью finally и withContext(NonCancellable). Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Очистка с finally и NonCancellable»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Кооперативная отмена: isActive и ensureActive
- withTimeout и withTimeoutOrNull
- Очистка с finally и NonCancellable
- Распространение отмены в иерархиях сопрограмм