我有以下代码片段:
@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
我有几个问题:
为什么CoroutineExceptionHandler
不处理该异常以及为什么将其传播?
如果我注释掉if (id % 5 == 0) throw IllegalArgumentException()
行,我仍然可以在控制台中看到打印的ID 11、22、33等。似乎cancel()
不会立即中断协程。是这样吗?
答案 0 :(得分:1)
为什么
CoroutineExceptionHandler
不处理该异常以及为什么将其传播?
那是它的合同。名称“ handler”实际上是一个用词不当,因为它不处理异常,只观察,并且仅在异常已经丢失的情况下才会丢失来自顶级协程。您已将处理程序安装到子协程中,在此无效。实际上,它是在其documentation的开头指定的:
interface CoroutineExceptionHandler : Element (source)
协程上下文中的一个可选元素,用于处理未捕获异常。
通常,未捕获的异常只能由使用启动构建器创建的根协程产生。所有子协程(在另一个Job上下文中创建的协程)将对其异常的处理委托给其父协程,该协程也委托给父协程,依此类推,直到根为止,因此从不使用在其上下文中安装的CoroutineExceptionHandler。 >
如果我注释掉
if (id % 5 == 0) throw IllegalArgumentException()
行,我仍然可以在控制台中看到打印的ID 11、22、33等。似乎cancel()
不会立即中断协程。是这样吗?
取消是一种合作机制。 cancel()
的唯一直接作用就是降低协程的isActive
标志。运行时只有在没有悬浮点的情况下才可以读取标志(send
甚至不是suspend fun
)。