Kotlin 协同程序:runBlocking与coroutineScope

Kotlin 协同程序:runBlocking与coroutineScope,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,我在阅读,试图理解和学习它 其中有一部分代码为: fun main() = runBlocking { // this: CoroutineScope launch { delay(200L) println("Task from runBlocking") } coroutineScope { // Creates a new coroutine scope launch { delay(900

我在阅读,试图理解和学习它

其中有一部分代码为:

fun main() = runBlocking { // this: CoroutineScope
    launch { 
        delay(200L)
        println("Task from runBlocking")
    }

    coroutineScope { // Creates a new coroutine scope
        launch {
            delay(900L) 
            println("Task from nested launch")
        }

        delay(100L)
        println("Task from coroutine scope") // This line will be printed before nested launch
    }

    println("Coroutine scope is over") // This line is not printed until nested launch completes
}
输出如下所示:

Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
我的问题是为什么会这样:

 println("Coroutine scope is over") // This line is not printed until nested launch completes
总是最后一个吗

既然:

coroutineScope { // Creates a new coroutine scope
    ....
}
停赛了吗

这里还有一个注释:

runBlocking和coroutineScope之间的主要区别在于后者在等待所有子线程完成时不会阻塞当前线程

我不明白coroutineScope和runBlocking在这里有什么不同?coroutineScope看起来像是它的阻塞,因为它只在完成时到达最后一行

这里有人能给我点化一下吗


提前感谢。

runBlocking
阻塞当前线程,直到内部协同路由完成。在这里,执行
runBlocking
的线程将被阻塞,直到
coroutineScope
中的协程完成

首先
launch
只是不允许线程执行
runBlocking
之后的指令,而是允许继续执行该
launch
block之后立即出现的指令-这就是为什么在运行blocking中的
任务之前打印来自coroutine范围的
任务的原因

但是
runBlocking
上下文中的嵌套
coroutineScope
将不允许线程执行此
coroutineScope
块之后的指令,因为
runBlocking
将阻塞线程,直到
coroutineScope
中的coroutine完全完成。这就是为什么
Coroutine作用域结束了
总是在嵌套启动的
任务之后出现

我不明白coroutineScope和runBlocking在这里有什么不同?coroutineScope看起来像是它的阻塞,因为它只在完成时到达最后一行

从代码块的角度来看,您的理解是正确的。
runBlocking
coroutineScope
之间的区别发生在一个较低的级别:当coroutine被阻塞时线程发生了什么

  • runBlocking
    不是一种
    暂停乐趣
    。调用它的线程将保留在其中,直到协同例程完成

  • coroutineScope
    是一个
    suspend-fun
    。如果您的协同程序挂起,
    coroutineScope
    函数也会挂起。这允许顶级函数(创建协同程序的非挂起函数)继续在同一线程上执行。线程已“转义”了
    coroutineScope
    块,并准备执行其他一些工作

在您的特定示例中:当您的
coroutineScope
挂起时,控件返回到
runBlocking
中的实现代码。这段代码是一个事件循环,它驱动您在其中启动的所有协程。在您的情况下,将有一些协同程序计划在延迟后运行。当时间到达时,它将恢复适当的协同程序,该程序将运行一段时间,挂起,然后控制将再次进入
runBlocking


虽然上面描述了概念上的相似性,但它也应该向您展示
runBlocking
是一种与
coroutineScope
完全不同的工具

  • runBlocking
    是一种低级构造,仅用于框架代码或像您这样的自包含示例中。它将现有线程转换为事件循环,并创建其与
    调度程序的协程,该调度程序将恢复协程发布到事件循环的队列中

  • coroutineScope
    是一种面向用户的构造,用于描绘正在并行分解的任务的边界。您可以使用它方便地等待它内部发生的所有
    async
    工作,获得最终结果,并在一个中心位置处理所有故障


    • 来自这篇精彩的文章

      suspend-fun-Iterable.pmap(f:suspend(A)->B):List=coroutineScope{
      map{async{f(it)}}.awaitAll()
      }
      
      使用runBlocking时,我们没有使用结构化并发,因此f的调用可能会失败,而所有其他执行都会继续进行。而且,我们对代码的其余部分也没有很好地处理。通过使用runBlocking,我们强制阻塞线程,直到pmap的整个执行完成,而不是让调用方决定执行的方式


      选择的答案很好,但未能解决所提供示例代码的其他一些重要方面。例如,启动是非阻塞的,并且应该立即执行。这根本不是事实。启动本身会立即返回,但启动中的代码看起来确实被放入了队列中,并且只有在之前放入队列的任何其他启动完成时才会执行

      下面是一段类似的示例代码,删除了所有延迟,并包含了一个额外的启动。不看下面的结果,看看是否可以预测数字的打印顺序。你很可能会失败:

      import kotlinx.coroutines.*
      
      fun main() = runBlocking {
          launch { 
              println("1")
          }
      
          coroutineScope {
              launch {
                  println("2")
              }
      
              println("3") 
          }
      
          coroutineScope {
              launch {
                  println("4")
              }
      
              println("5")
          }
      
          launch { 
              println("6")
          }
      
          for (i in 7..100) {
              println(i.toString())
          }
      
          println("101")
      }
      
      结果是:

      3
      1
      2
      5
      4
      7
      8
      9
      10
      ...
      99
      100
      101
      6
      
      即使在执行了将近100个println之后,数字6仍然是最后打印的,这一事实表明,在启动完成之后,直到所有非阻塞代码完成之后,最后一次启动中的代码才被执行。但这也不是真的,因为如果是这样的话,第一次发射应该在7号到101号完成之前执行。底线是什么?混合使用launch和coroutineScope是非常不可预测的,如果您希望事情的执行方式有一定的顺序,那么应该避免使用

      证明启动中的代码被放入队列,并且只有在所有非阻塞代码完成后才执行
      3
      1
      2
      5
      4
      7
      8
      9
      10
      ...
      99
      100
      101
      6
      
      import kotlinx.coroutines.*
      
      fun main() = runBlocking {
          launch { 
              println("1")
          }
      
          launch { 
              println("2")
          }
      
          launch { 
              println("3")
          }
      
          for (i in 4..100) {
              println(i.toString())
          }
      
          println("101")
      }
      
      4
      5
      6
      ...
      101
      1
      2
      3