【问题标题】:Exception handling in coroutines and cancellation协程中的异常处理和取消
【发布时间】:2020-11-09 14:16:36
【问题描述】:

我有以下代码片段:

@UseExperimental(ExperimentalCoroutinesApi::class)
fun main() {
    fun CoroutineScope.send(id: Int) {
        if (id % 11 == 0) cancel()
        if (id % 5 == 0) throw IllegalArgumentException()
        println(id)
    }

    fun send() = runBlocking {
        val handler = CoroutineExceptionHandler { _, _ ->
            println("Something bad happened")
        }
        val jobs = (1..1000).map {
            it to launch(handler) {
                send(it)
            }
        }.toMap()
        jobs.values.joinAll()
        println("Unable to send message to ids: ${jobs.filterValues { it.isCancelled }.keys.joinToString()}")
    }

    send()
}

当我运行此代码时,我得到以下结果:

1
2
3
4
Exception in thread "main" java.lang.IllegalArgumentException

我有几个问题:

  1. 为什么CoroutineExceptionHandler 不处理异常以及为什么它被传播?

  2. 如果我注释掉 if (id % 5 == 0) throw IllegalArgumentException() 行,我仍然可以在控制台中看到打印的 ID 11、22、33 等。似乎cancel() 并没有立即中断协程。是这样吗?

【问题讨论】:

    标签: kotlin kotlin-coroutines


    【解决方案1】:

    1.

    为什么CoroutineExceptionHandler 不处理异常以及为什么它被传播?

    那是它的契约。 “handler”这个名字实际上是用词不当,因为它不处理异常,它只观察它,而且它只在异常即将丢失、已经逃逸的地方这样做来自顶级协程。您已将处理程序安装到子协程中,但它不起作用。这实际上是在其documentation 的开头指定的:

    interface CoroutineExceptionHandler : Element (source)

    协程上下文中的可选元素,用于处理未捕获异常。

    通常,未捕获的异常只能由使用启动构建器创建的根协同程序产生。所有子协程(在另一个 Job 的上下文中创建的协程)将它们的异常处理委托给它们的父协程,父协程也委托给父协程,依此类推,直到根,所以永远不会使用安装在它们的上下文中的 CoroutineExceptionHandler。

    2.

    如果我注释掉 if (id % 5 == 0) throw IllegalArgumentException() 行,我仍然可以在控制台中看到打印的 ID 11、22、33 等。似乎cancel() 并没有立即中断协程。是这样吗?

    取消是一种合作机制。 cancel() 的唯一直接效果是降低协程的 isActive 标志。运行时有机会仅在暂停点内读取标志,而您没有(send 甚至不是suspend fun)。

    【讨论】:

      猜你喜欢
      • 2022-01-18
      • 2019-10-17
      • 1970-01-01
      • 2019-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多