CoroutineScope не может быть повторно использован после исключения - PullRequest
0 голосов
/ 31 января 2020

У меня есть класс, который реализует CoroutineScope, и у него есть функция run, в которой есть один параметр для проверки, хочу ли я вызвать исключение. В этом примере я обнаружил, что блок кода внутри launch во втором вызове run фактически не выполняется. Это намеренное поведение? Мне потребовалось некоторое время, чтобы выяснить проблему в моем исходном коде, потому что журнал ничего не говорит, пока я не написал пример кода для тестирования. Если это предусмотрено, какова лучшая практика для решения проблемы? Чего я хочу добиться, так это повторно использовать функцию run и уметь перехватывать исключения внутри этой функции.

package coroutine.exceptions

import kotlinx.coroutines.*

fun log(msg: String) = println("$msg (${Thread.currentThread().name})")
val exceptionHandler = CoroutineExceptionHandler { _, e ->
    log(e.localizedMessage)
}

fun main() = runBlocking {

    val test1 = TestReuseCoroutineAfterException("test1")
    test1.run(true)
    delay(2000)
    test1.run(false)
    delay(2000)

    log("finished")
}


class TestReuseCoroutineAfterException(private val testName: String) :
    CoroutineScope by CoroutineScope(Dispatchers.Default) {

    fun run(throwException: Boolean) {
        log("$testName: call - started")
        launch(exceptionHandler) {
            if (throwException)
                throw Exception("$testName: call - throw exception")
            else
                log("$testName: call - done")
        }
        log("$testName: call - ended")
    }

}

Вывод:

test1: call - started (main)
test1: call - ended (main)
test1: call - throw exception (DefaultDispatcher-worker-1)
test1: call - started (main)
test1: call - ended (main)
finished (main)

Process finished with exit code 0

1 Ответ

1 голос
/ 31 января 2020

Когда вы создаете CoroutineScope следующим образом:

CoroutineScope(Dispatchers.Default)

Он использует Job() в качестве своего задания, а Job() отменяет область действия, если исключение было вызвано в дочерней сопрограмме. Если вы не хотите отменять всю область видимости в случае отказа ребенка, используйте SupervisorJob :

CoroutineScope(Dispatchers.Default + SupervisorJob())

. После этого изменения выведите код:

test1: call - started (main)
test1: call - ended (main)
test1: call - throw exception (DefaultDispatcher-worker-1)
test1: call - started (main)
test1: call - ended (main)
test1: call - done (DefaultDispatcher-worker-2)
finished (main)
...