我正在尝试并行运行多个任务,但是这些任务是独立的,因此如果子协程之一失败,我也不希望其 sibling 或 parent 也失败。在下面的代码中,我使用coroutineScope创建了一个新范围,在这些范围内运行这些任务,并在发送其ID和应等待的延迟时间时启动了5个异步任务。第二个协程抛出异常。在这种情况下,代码执行了我想要的操作,它计算成功完成的作业和失败的作业的总和,返回0。

但是,我阅读的内容是,kotlinx库中还有一个supervisorScope,而不是coroutineScope(如果不处理异常,它会取消父/同级兄弟),因为它们不依赖其他任务,因此应该优先使用supervisorScope。我不确定为什么要随着使用coroutineScope获得想要的结果而更改为使用supervisorScope。

Q1 :如果我要更改为supervisorScope,我的异步块中是否应该进行某些更改?

Q2 :是否可以在异步块内捕获任何异常,并且不让任何东西传播到其父对象?我知道您也可以在.await()阶段捕获异常,但这是应该完成的方式吗?

runBlocking {
    coroutineScope {
            val job1 = async<Int> {
                try {
                    request(1, 1000)
                } catch (e: Exception) {
                    println("Job 1 failed with $e")
                    0
                }
            }

            val job2 = async<Int> {
                try {
                    request(2, 2000)
                    throw Exception("cancelling Job 2")
                } catch (e: Exception) {
                    println("Job 2 failed: $e")
                    0
                }
            }

            val job3 = async {
                try {
                    request(3, 3000)
                } catch (e: Exception) {
                    println("Job 3 failed with $e")
                    0
                }
            }

            val job4 = async {
                try {
                    request(4, 4000)
                } catch (e: Exception) {
                    println("Job 4 failed with $e")
                    0
                }
            }

            val job5 = async {
                try {
                    request(5, 5000)
                } catch (e: Exception) {
                    println("Job 5 failed with $e")
                    0
                }
            }

            val result = job1.await() + job2.await() + job3.await() + job4.await() + job5.await()
            println(result.toString())
        }

        println("Finished")
}

suspend fun request(id: Int, time: Long): Int {
    println("Job $id started")
    delay(time)
    println("Job $id finished")
    return id
}

最佳答案

所有协程运行完成的原因是,您用catch作业2本身内的作业2抛出的异常,因此它永远不会传播到Job的层次结构中,因此什么也没有发生。

但是,如果您删除job2中的catch子句,则将始终取消job [1-5],而与使用coroutineScopesupervisorScope无关。

这是因为job2.await()会抛出异常。由于这是在job [1-5]的父作业中发生的(即在顶部coroutineScope / supervisorScope中),并且由于失败/取消的父作业总是会取消子作业,因此job [1-5]也将被取消。

A1 :不使用coroutineScopesupervisorScope,删除coroutineScope并将其直接放在runBlocking下。

A2 :当然可以允许在async { }中捕获异常,以确保它适合您的用例,从而不会在.await()中发生。

10-07 23:34