如何在使用当前父范围的“暂停乐趣”中启动 Kotlin 协程?

How to launch a Kotlin coroutine in a `suspend fun` that uses the current parent Scope?

如何从挂起函数启动协程并让它使用当前作用域? (这样 Scope 在启动的协程也结束之前不会结束)

我想写类似下面的东西 –

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
    go()
}

suspend fun go() {
    launch {
        println("go!")
    }
}

但这有一个语法错误:"Unresolved Reference: launch"。看起来 launch 必须是 运行 以下列方式之一 –

GlobalScope.launch {
    println("Go!")
}

runBlocking {
    launch {
        println("Go!")
    }
}

withContext(Dispatchers.Default) {
    launch {
        println("Go!")
    }
}

coroutineScope {
    launch {
        println("Go!")
    }
}

None 这些替代方案可以满足我的需要。代码 "blocks" 而不是 "spawning",或者它产生但父作用域不会在父作用域本身结束之前等待它完成。

我需要它 "spawn" 在当前的父协程作用域中启动(启动),并且该父作用域应该等待生成的协程在它自己结束之前完成。

我预计 suspend fun 中的简单 launch 将有效并使用其父范围。

我正在使用 Kotlin 1.3cotlinx-coroutines-core:1.0.1

我相信我找到了解决方案,那就是 with(CoroutineScope(coroutineContext)。以下示例说明了这一点 –

import kotlinx.coroutines.*

fun main() = runBlocking {
    go()
    go()
    go()
    println("End")
}

suspend fun go() {
//  GlobalScope.launch {                     // spawns, but doesn't use parent scope
//  runBlocking {                            // blocks
//  withContext(Dispatchers.Default) {       // blocks
//  coroutineScope {                         // blocks
    with(CoroutineScope(coroutineContext)) { // spawns and uses parent scope!
        launch {
            delay(2000L)
            println("Go!")
        }
    }
}

不过,Rene 在上面发布了一个更好的解决方案。

您应该使函数 go 成为 CoroutineScope 的扩展函数:

fun main() = runBlocking {
    go()
    go()
    go()
    println("End")
}

fun CoroutineScope.go() = launch {
    println("go!")
}

阅读此 article 了解为什么在 suspend 函数中启动其他协程而不创建新的 coroutineScope{}.

不是一个好主意

惯例是:在一个suspend函数中调用其他suspend函数并创建一个新的CoroutineScope,如果你需要启动并行协程。结果是,当所有新启动的协程都完成(结构化并发)时,协程只会 return。

另一方面,如果您需要在不知道作用域的情况下启动新协程,您可以创建一个 CoroutineScope 的扩展函数,它本身不是 suspendable。现在调用者可以决定应该使用哪个范围。

假设您正在处理一些 RxJava Observable,现在不是重构它们的时候,您现在可以通过这种方式获取挂起函数的 CoroutineScope:

suspend fun yourExtraordinarySuspendFunction() = coroutineScope {
    val innerScope = this // i.e. coroutineScope
    legacyRxJavaUggh.subscribe { somePayloadFromRxJava ->
        innerScope.launch {
            // TODO your extraordinary work
        }
    }
}