Почему приостанавливающие функции генерируют исключения в наконец - PullRequest
0 голосов
/ 03 марта 2019

Как видно из заголовка, почему функции приостановки генерируют исключения в finally?

Со стандартными функциями блок finally выполняет все из них:

import kotlinx.coroutines.*

fun main() {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("Caught $exception")
    }
    val job = GlobalScope.launch(handler) {
        launch {
            // the first child
            try {
                println("inside try")
                delay(1000)
            } finally {

                println("Children are cancelled, but exception is not handled until all children terminate")

                Thread.sleep(1000)
                println("thread.sleep executed")
                //foo()
                println("The first child finished its non cancellable block")

            }
        }
        launch {
            // the second child
            delay(10)
            println("Second child throws an exception")
            throw ArithmeticException()
        }
    }

    Thread.sleep(1000000)
    println("complete")
}

Здесь, например, когда я делаю Thread.sleep(1000), он печатает:

«Первый дочерний элемент завершил свой неотменяемый блок»

, но если я изменю эту строку на delay(1000), это не так.

Насколько я понимаю, в finally -блоке исключение, если оно существует, выдается после выполнения всего блока.

Но в этом случае delay приводит к тому, что это исключение выдается рано.

С другой стороны, Thread.sleep нет.

Может кто-нибудь помочь объяснить?

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

Насколько я понимаю, в finally -блоке исключение, если оно существует, выдается после выполнения всего блока.

Это не так.Если блок finally выдает исключение, это приводит к внезапному завершению блока finally с этим исключением.Любое исключение, которое было сгенерировано в пределах try, таким образом, отбрасывается.Это именно то, что происходит в вашем случае: блок finally первой дочерней сопрограммы получает CancellationException в строке delay(1000).Thread.sleep(1000) - блокирующая, неотменяемая функция, поэтому она не наблюдает отмену.

Вы, вероятно, перепутали это с тем фактом, что, если блок try выдает исключение, то сначалаполный блок finally выполняется перед выдачей исключения.Блок finally необходим для нормального завершения, чтобы это произошло.

Поэтому я полагаю, что вы не описываете никаких различий в поведении простых и приостановленных функций.

0 голосов
/ 05 марта 2019

Функции приостановки в Kotlin работают иначе, чем функции блокировки.Когда вы отменяете Job, при первом приостановлении после отмены выполнение будет остановлено, даже , если вы находитесь в блоке finally.Если вы используете Thread.sleep(1000) вместо delay(1000) в своем блоке finally, приостановки не происходит, потому что Thread.sleep() означает блокирование , а не приостановку, поэтому весь ваш блок finally будет выполнен,

Обратите внимание, что использование функций блокировки внутри функций приостановки является анти-паттерном и его следует избегать !!

Чтобы достичь желаемого поведения без использования функций блокировки, используйте withContext(NonCancellable) {...}, как описано здесь .

Ваш пример кода должен выглядеть следующим образом:

fun main() {
  val handler = CoroutineExceptionHandler { _, exception ->
    println("Caught $exception")
  }
  val job = GlobalScope.launch(handler) {
    launch {
      // the first child
      try {
        println("inside try")
        delay(1000000)
      } finally {
        withContext(NonCancellable) {
          println("Children are cancelled, but exception is not handled until all children terminate")

          delay(1000) // This suspension cannot be cancelled
          println("delay executed")
          //foo()
          println("The first child finished its non cancellable block")
        }
      }
    }
    launch {
      // the second child
      delay(10)
      println("Second child throws an exception")
      throw ArithmeticException()
    }
  }

  Thread.sleep(1000000)
  println("complete")
}

Вывод:

inside try
Second child throws an exception
Children are cancelled, but exception is not handled until all children terminate
delay executed
The first child finished its non cancellable block
Caught java.lang.ArithmeticException
...