协程中的异常处理和取消

时间:2020-07-20 10:35:12

标签: kotlin kotlin-coroutines

我有以下代码片段:

@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()不会立即中断协程。是这样吗?

1 个答案:

答案 0 :(得分: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)。