Arc*_*nes 34 kotlin kotlin-coroutines
我正在阅读Coroutine Basics,试图理解和学习它.
这段代码有一部分:
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
}
Run Code Online (Sandbox Code Playgroud)
输出如下:
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
Run Code Online (Sandbox Code Playgroud)
我的问题是为什么这一行:
println("Coroutine scope is over") // This line is not printed until nested launch completes
Run Code Online (Sandbox Code Playgroud)
被称为永远?
不应该被称为自:
coroutineScope { // Creates a new coroutine scope
....
}
Run Code Online (Sandbox Code Playgroud)
暂停了?
那里还有一张纸条:
runBlocking和coroutineScope之间的主要区别在于后者在等待所有子项完成时不会阻塞当前线程.
我不明白coroutineScope和runBlocking在这里有什么不同?coroutineScope看起来像它的阻塞,因为它只在完成时才到达最后一行.
谁能在这里启发我?
提前致谢.
And*_*Dev 32
选择的答案很好,但未能解决所提供的示例代码的其他一些重要方面。例如,launch 是非阻塞的并且应该立即执行。那明显是错的。启动本身立即返回,但启动内部的代码似乎确实被放入队列中,并且仅在之前放入队列的任何其他启动已完成时才执行。
这是一段类似的示例代码,其中删除了所有延迟并包含了额外的启动。不用看下面的结果,看看你能不能预测出数字的打印顺序。您可能会失败:
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")
}
Run Code Online (Sandbox Code Playgroud)
结果是:
3
1
2
5
4
7
8
9
10
...
99
100
101
6
Run Code Online (Sandbox Code Playgroud)
最后打印数字 6 的事实,即使在执行了近 100 次 println 之后,也表明在启动后所有非阻塞代码完成之前,上次启动中的代码永远不会被执行。但这也不是真的,因为如果是这样的话,第一次发射不应该在数字 7 到 101 完成之前执行。底线?混合使用 launch 和 coroutineScope 是高度不可预测的,如果您期望执行某事的方式有一定的顺序,则应该避免。
为了证明launchs里面的代码被放入一个队列并且只在所有非阻塞代码完成后才执行,运行这个(不使用coroutineScopes):
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")
}
Run Code Online (Sandbox Code Playgroud)
这是你得到的结果:
4
5
6
...
101
1
2
3
Run Code Online (Sandbox Code Playgroud)
添加 CoroutineScope 将破坏这种行为。它将导致 CoroutineScope 之后的所有非阻塞代码在 CoroutineScope 之前的所有代码完成之前不会被执行。
还应该注意的是,在此代码示例中,队列中的每个启动都按照它们添加到队列中的顺序依次执行,并且每次启动只会在前一个启动执行后执行。这可能会使所有启动看起来共享一个公共线程。这不是真的。他们每个人都有自己的线程。但是,如果启动中的任何代码调用挂起函数,则在执行挂起函数时队列中的下一个启动将立即启动。老实说,这是非常奇怪的行为。为什么不异步运行队列中的所有启动?虽然我不知道这个队列中发生了什么的内部结构,但我的猜测是队列中的每个启动都没有自己的线程,而是共享一个公共线程。只有当遇到挂起函数时,才会为队列中的下一次启动创建一个新线程。可以通过这种方式来节省资源。
总而言之,执行按以下顺序完成:
Mar*_*nik 30
我不明白coroutineScope和runBlocking在这里有什么不同?coroutineScope看起来像它的阻塞,因为它只在完成时才到达最后一行.
从块中代码的角度来看,您的理解是正确的.runBlocking
和之间的区别coroutineScope
发生在较低级别:协程被阻止时线程发生了什么?
runBlocking
不是suspend fun
.调用它的线程保留在其中,直到协程完成.
coroutineScope
是一个suspend fun
.如果您的协程暂停,该coroutineScope
函数也会被暂停.这允许顶级函数(一个创建协同程序的非挂起函数)继续在同一个线程上执行.该线程已"逃脱"该coroutineScope
块,并准备做其他工作.
在您的具体示例中:当您coroutineScope
暂停时,控件返回到里面的实现代码runBlocking
.此代码是一个事件循环,它驱动您在其中启动的所有协同程序.在你的情况下,会有一些协同程序计划在延迟后运行.当时间到来时,它将恢复相应的协程,该协程将运行一段时间,暂停,然后再次控制内部runBlocking
.
虽然上面描述了概念上的相似之处,但它也应该向您展示这runBlocking
是一个完全不同的工具coroutineScope
.
runBlocking
是一个低级构造,仅用于框架代码或像您这样的自包含示例.它将现有线程转换为事件循环并创建其协程,并将协程Dispatcher
恢复到事件循环的队列.
coroutineScope
是一个面向用户的构造,用于描述在其中并行分解的任务的边界.您可以使用它方便地等待其中async
发生的所有工作,获得最终结果,并在一个中心位置处理所有故障.
runBlocking
只是阻塞当前线程,直到内部协程完成。在这里,执行的线程runBlocking
将被阻塞,直到协程coroutineScope
结束为止。
首先launch
只是不允许线程执行后面的指令runBlocking
,但允许继续执行此launch
块之后的指令-这就是为什么Task from coroutine scope
在than之前打印的原因Task from runBlocking
。
但是嵌套coroutineScope
在的上下文runBlocking
中将不允许线程执行此coroutineScope
块之后的指令,因为runBlocking
它将阻塞线程,直到协程coroutineScope
完全完成为止。这就是为什么Coroutine scope is over
总会来的原因Task from nested launch
。
归档时间: |
|
查看次数: |
3880 次 |
最近记录: |