我正在尝试并行运行多个任务,但是这些任务是独立的,因此如果子协程之一失败,我也不希望其 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],而与使用coroutineScope
或supervisorScope
无关。
这是因为job2.await()
会抛出异常。由于这是在job [1-5]的父作业中发生的(即在顶部coroutineScope
/ supervisorScope
中),并且由于失败/取消的父作业总是会取消子作业,因此job [1-5]也将被取消。
A1 :不使用coroutineScope
或supervisorScope
,删除coroutineScope
并将其直接放在runBlocking
下。
A2 :当然可以允许在async { }
中捕获异常,以确保它适合您的用例,从而不会在.await()
中发生。