在协程范围内抛出异常时,协程范围是否可重用?

Col*_*ard 1 error-handling kotlin kotlin-coroutines

我一直在解决使用协同程序进行错误处理的问题,我已通过以下步骤缩小到此单元测试:

  1. 我与任何调度员一起创建一个协程范围.
  2. 我在异步块中(甚至在嵌套的异步块中)在此范围内的任何位置抛出异常.
  3. 我在返回的延迟值上调用await并处理异常.
  4. 这一切都很好.但是,当我尝试使用相同的协同程序范围来启动新的协程时,这总是异常地完成同样的异常.

    这是测试:

    fun `when you throw an exception in a coroutine scope, is the coroutine scope dead?`() {
        val parentJob = Job()
        val coroutineScope = CoroutineScope(parentJob + Dispatchers.Default)
    
        val deferredResult = coroutineScope.async { throw IllegalStateException() }
    
        runBlocking {
            try {
                deferredResult.await()
            } catch (e: IllegalStateException) {
                println("We caught the exception. Good.")
            }
    
            try {
                coroutineScope.async { println("we can still use the scope") }.await()
            } catch (e: IllegalStateException) {
                println("Why is this same exception still being thrown?")
            }
    
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

以下是测试的输出:

We caught the exception. Good.
Why is this same exception still being thrown?
Run Code Online (Sandbox Code Playgroud)
  • 为什么会这样?

    • 我的理解是你可以正常处理异常并用协同程序从中恢复.
  • 我该如何处理异常?

    • 我需要创建一个新的coroutineScope吗?
    • 如果我想继续使用相同的coroutineScope,我是否可以永远不会抛出异常?
    • 我应该回来Either<Result, Exception>吗?
    • 我已经尝试过使用CoroutineExceptionHandler,但我仍然得到相同的结果.

注意我正在使用Kotlin 1.3

Rom*_*rov 5

当您在一个范围内启动协程(使用asynclaunch),则默认情况下协同程序的故障取消此范围内及时取消所有其他的孩子.这种设计避免了悬空和丢失异常.

这里的一般建议是:

  • 除非你真的需要并发,否则不要使用async/ await.当您使用挂起函数设计代码时,不需要使用asyncawait.

  • 如果确实需要并发执行,请遵循以下模式:

    coroutineScope { 
        val d1 = async { doOne() }
        val d2 = async { doTwo() }
        ...
        // retrieve and process results
        process(d1.await(), d2.await(), .... )
    }
    
    Run Code Online (Sandbox Code Playgroud)

如果你需要处理的并发操作的失败,然后把try { ... } catch { ... }周围coroutineScope { ... }搭上故障在任何并发执行的操作.