Отменяемая сопрограмма отменяется

#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 . Лично я никогда не использовал ее — сложнее рассуждать и легче вводить ошибки — есть (всегда?) Лучшие способы сделать то же самое.