关于异步:使用Kotlin协程对独立异步任务进行并行分解

Parallel decomposition of independent async tasks with kotlin coroutines

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

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

问题1:如果我要更改为supervisorScope,我的异步块中是否应该有所更改?

问题2:是否可以在async块中捕获任何异常,并且不让任何内容传播到其父级?我知道您也可以在.await()阶段捕获异常,但这是应该这样做的方式吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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")
}
1
2
3
4
5
6
suspend fun request(id: Int, time: Long): Int {
    println("Job $id started")
    delay(time)
    println("Job $id finished")
    return id
}


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

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

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

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

解答2:当然可以允许在async { }内捕获异常,以确保它不出现在.await()中(如果适合您的用例)。