如何并行运行多个 Kotlin 协程并等待它们完成后再继续

ale*_*btr 15 kotlin kotlin-coroutines

我需要并行运行 2 个协程并等待它们完成后再继续。下面的代码可以工作,但它使用的GlobalScope不是最好的方法。

有没有更好的办法?

fun getInfo(onSuccess: () -> Unit, onError: () -> Unit) {
        GlobalScope.launch(Dispatchers.IO) {
            try {
                coroutineScope {
                    launch { getOne() }
                    launch { getTwo() }
                }
                onSuccess.invoke()
            } catch (e: Throwable) {
                onError.invoke()
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Wil*_*zel 18

我建议实现getInfo为一个挂起函数,它知道它应该运行的上下文。这样,从哪个上下文调用它就无关紧要了(*)。

此外,我不会使用回调来继续。您可以根据getInfo()回报来决定如何继续 (**)。

这实际上是协程最伟大的事情,您可以将基于回调的代码转换为读起来像顺序代码的代码。

getOne()由于您不关心 和的结果getTwo(),因此使用launch是正确的方法。它返回一个Job. 您可以挂起协程,直到两个函数都完成,joinAll()然后可以在 上调用它们Collection<Job>

suspend fun getInfo() = withContext(Dispatchers.IO) {
    try {
        listOf(
            launch { getOne() },
            launch { getTwo() }
        ).joinAll()
        false
    } catch (e: Throwable) {
        true
    }
}
Run Code Online (Sandbox Code Playgroud)

您不需要使用GlobalScope,只需创建您自己的(***)。

我用作Defaultlaunch 的上下文getInfo,但任何其他上下文也可以,因为getInfo将在它应该运行的上下文上运行。

val newScope = CoroutineScope(Dispatchers.Default).launch {
    val error = getInfo()
    if(error) {
        onSuccess()
    } else {
        onError()
    }
}
// "newScope" can be cancelled any time
Run Code Online (Sandbox Code Playgroud)

* 万一我曾经Dispatcher.IO假装这两个函数正在执行一些长时间运行的 IO 工作。

** 我在这里使用了一个简单的布尔值,但当然你可以返回更有意义的东西。

*** 或挂钩到生命周期感知的框架给出的某个范围


Ten*_*r04 5

您可以使用任何您喜欢的范围。这并不影响你的问题。GlobalScope 不受欢迎,因为它没有封装您的任务。

实际使用哪个范围来启动协程将完全取决于您正在执行的操作的上下文以及您正在使用的封装策略。

要一次启动多个协程并等待所有协程,请使用asyncawait()。如果您只需要在继续之前完成所有这些操作,则可以使用awaitAll()它们的列表。

使用 IO 调度程序来运行回调似乎很奇怪,但我会保留它,因为我对代码的上下文一无所知。

fun getInfo(onSuccess: () -> Unit, onError: () -> Unit) {
        GlobalScope.launch(Dispatchers.IO) {
            try {
                coroutineScope {
                    listOf(
                        async { getOne() }
                        async { getTwo() }
                    }.awaitAll()
                }
                onSuccess.invoke()
            } catch (e: Throwable) {
                onError.invoke()
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)