【问题标题】:How to launch a Kotlin coroutine in a `suspend fun` that uses the current parent Scope?如何在使用当前父 Scope 的“暂停乐趣”中启动 Kotlin 协程?
【发布时间】:2019-05-20 15:08:02
【问题描述】:

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

我想写一些类似下面的东西——

import kotlinx.coroutines.*

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

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

但这有一个语法错误:“未解决的参考:启动”。看来launch 必须以下列方式之一运行——

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

或者

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

或者

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

或者

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

这些替代方案都不能满足我的需要。代码“阻塞”而不是“生成”,或者它生成但父作用域不会在父作用域本身结束之前等待其完成。

我需要它在当前父协程作用域中“生成”(启动),并且该父作用域应该等待生成的协程完成后再结束。

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

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

【问题讨论】:

    标签: kotlin kotlinx.coroutines


    【解决方案1】:

    你应该让函数go成为CoroutineScope的扩展函数:

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

    阅读此article 以了解为什么不创建新的coroutineScope{} 就从suspend 函数和其他协程开始不是一个好主意。

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

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

    【讨论】:

      【解决方案2】:

      我相信我找到了解决方案,即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 在上面发布了一个更好的解决方案。

      【讨论】:

      • 这可行,但有点倒退。它使用全局suspend val coroutineContext 并用它实例化CoroutineScope。为什么不声明suspend fun CoroutineScope.go(),这是推荐的方式?另外,您确定需要go() 成为suspend fun 吗?它实际上并没有暂停。
      • 将其设为fun CoroutineScope.go() 更简洁,不,它不需要是suspend fun。我只是想了解 Kotlin 的协程,谢谢。
      猜你喜欢
      • 2018-08-14
      • 2020-01-04
      • 1970-01-01
      • 2019-11-22
      • 1970-01-01
      • 1970-01-01
      • 2022-07-25
      • 1970-01-01
      • 2020-04-05
      相关资源
      最近更新 更多