#kotlin #kotlin-coroutines
#kotlin #kotlin-сопрограммы
Вопрос:
Я пытаюсь поэкспериментировать с не отменяемыми сопрограммами и написал следующий код:
fun main(): Unit = runBlocking {
// launch first coroutine
coroutineScope {
val job1 = launch {
withContext(NonCancellable) {
val delay = Random.nextLong(from = 500, until = 5000)
println("Coroutine started. Waiting for ${delay}ms")
delay(delay)
println("Coroutine completed")
}
}
delay(300) // let it print the first line
println("Cancelling coroutine")
job1.cancelAndJoin()
}
}
Вывод:
Coroutine started. Waiting for 1313ms
Cancelling coroutine
Coroutine completed
Пока все работает так, как ожидалось. Однако, если я передаю NonCancellable
контекст (или, скорее, Job
) непосредственно в launch
функцию, поведение изменяется, и сопрограмма отменяется:
fun main(): Unit = runBlocking {
// launch first coroutine
coroutineScope {
val job1 = launch(context = NonCancellable) {
val delay = Random.nextLong(from = 500, until = 5000)
println("Coroutine started. Waiting for ${delay}ms")
delay(delay)
println("Coroutine completed")
}
delay(300) // let it print the first line
println("Cancelling coroutine")
job1.cancelAndJoin()
}
}
Вывод:
Coroutine started. Waiting for 4996ms
Cancelling coroutine
Почему второй фрагмент выдает другой результат?
Ответ №1:
Задание, которое вы передаете в качестве аргумента launch
методу, является не заданием запущенной сопрограммы, а ее родительским заданием.
Во втором фрагменте выше NonCancellable
приведено родительское задание job1
. Поскольку job1
это обычная работа, ее можно отменить (но ее родительский элемент — нет).
Комментарии:
1. спасибо за разъяснение! Но тогда какова цель этого родительского задания? Насколько я понимаю, это
launch
позволяет создавать только 1 сопрограмму, поэтому я не понимаю, как может быть полезно задание «контейнер». РЕДАКТИРОВАТЬ: о, может быть, мы могли бы добавить дополнительных дочерних элементов во второй момент, и в этом случае «контейнер» имел бы некоторую практическую полезность. Это предполагаемая цель?2. Родительское задание, как правило, используется для обеспечения структурированного параллелизма (например, для отмены дочерних сопрограмм при отмене их родительского элемента). Переданное задание
launch
используется для нарушения неявного структурированного параллелизма — вы явно выбираете новое родительское задание для сопрограммы вместо использования задания области, в которой вы вызываетеlaunch
. Лично я никогда не использовал ее — сложнее рассуждать и легче вводить ошибки — есть (всегда?) Лучшие способы сделать то же самое.