简短的回答
这是您正在做的一个更简单的版本,它避免了您可能遇到的同步问题:
suspend fun mainFunction(): List<A> {
return coroutineScope {
List(6) { async { func1() } }.awaitAll()
}
}
如果你想解开这个,你可以阅读长答案。我将解释原始代码中并非真正惯用且可以替换的不同内容。
长答案
问题中的代码中有多个非惯用的东西,所以我将尝试解决它们中的每一个。
基于 0 的范围的索引 for 循环
如果您只想多次重复一个操作,使用repeat(6)而不是for (j in 0..5)
. 它更容易阅读,尤其是当您不需要索引变量时:
suspend fun mainFunction(): List<A> {
var x: List<A> = listOf<A>()
val job = ArrayList<Job>()
val ans = mainScope.async {
repeat(6) {
job.add(
launch {
val res = async {
func1()
}
x += res.await()
}
)
}
job.joinAll()
}
ans.await()
return x
}
使用循环创建列表
如果您想要从该循环中创建一个列表,您还可以使用List(size) { computeElement() }
代替repeat
(或for
),它利用了List 工厂函数:
suspend fun mainFunction(): List<A> {
var x: List<A> = listOf<A>()
val ans = mainScope.async {
val jobs = List(6) {
launch {
val res = async {
func1()
}
x += res.await()
}
}
jobs.joinAll()
}
ans.await()
return x
}
额外的异步
无需在此处使用额外的异步包装您的启动,您可以launch
直接在 es 上使用您的范围:
suspend fun mainFunction(): List<A> {
var x: List<A> = listOf<A>()
val jobs = List(6) {
mainScope.launch {
val res = async {
func1()
}
x += res.await()
}
}
jobs.joinAll()
return x
}
异步 + 立即等待
使用async { someFun() }
然后立即await
-ing 这个Deferred
结果等同于直接调用someFun()
(除非您使用不同的范围或上下文,您在这里没有为最内部的逻辑执行此操作)。
所以你可以替换最里面的部分:
val res = async {
func1()
}
x += res.await()
通过 just x += func1()
,它给出:
suspend fun mainFunction(): List<A> {
var x: List<A> = listOf<A>()
val jobs = List(6) {
mainScope.launch {
x += func1()
}
}
jobs.joinAll()
return x
}
启动与异步
如果您想要结果,通常使用async
而不是launch
. 当您使用 时launch
,您必须手动将结果存储在某处(这会使您遇到像现在这样的同步问题)。有了async
,你得到一个Deferred<T>
值,你可以然后await()
,当你有一个Deferred
没有同步问题的列表时,当你等待它们时。
所以前面代码的总体思路是不好的做法,可能会咬你,因为它需要手动同步。您可以通过以下方式替换它:
suspend fun mainFunction(): List<A> {
val deferredValues = List(6) {
mainScope.async {
func1()
}
}
val x = deferredValues.awaitAll()
return x
}
或更简单:
suspend fun mainFunction(): List<A> {
return List(6) {
mainScope.async {
func1()
}
}.awaitAll()
}
手动连接与 coroutineScope
join()
手动作业通常是一种气味。如果你想等待一些协程完成,在一个coroutineScope { ... }
block中启动所有这些协程更为惯用,这将暂停直到所有子协程完成。
在这里,我们已经用调用 we 替换了所有 we launch
,所以这不再适用,因为我们仍然需要延迟值才能获得结果。但是,由于我们已经在挂起函数中,我们仍然可以使用而不是外部作用域来确保我们不会泄漏任何协程:join()
async
await
await()
coroutineScope
mainScope
suspend fun mainFunction(): List<A> {
return coroutineScope {
List(6) { async { func1() } }.awaitAll()
}
}