在runBlocking中使用coroutineScope有什么作用?

时间:2019-05-16 15:14:07

标签: kotlin kotlin-coroutines coroutinescope

在kotlin协程doc中,它解释了“ runBlocking和coroutineScope之间的差异”:

  

范围构建器

     

除了不同构建器提供的协程作用域之外,还可以使用coroutineScope构建器声明自己的作用域。它创建一个协程范围,直到所有启动的子级都完成才完成。 The main difference between runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete

我不太明白,示例代码显示了

import kotlinx.coroutines.*

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

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

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

println("+++ Coroutine scope is over") // This line is not printed until the nested launch completes
}

日志:

2019-05-16 10:47:45.107 12239-12239 +++ before enter runBlocking{}
2019-05-16 10:47:45.219 12239-12239 +++ Task from coroutine scope
2019-05-16 10:47:45.320 12239-12239 +++ Task from runBlocking
2019-05-16 10:47:45.620 12239-12239 +++ Task from nested launch
2019-05-16 10:47:45.621 12239-12239 +++ ---after exit runBlocking{}

+++ Task from nested launch会在+++ Task from runBlocking之后显示,因为它有500L的延迟。

但是,如果在launch块之后添加一些其他coroutineScop{}生成器,则结果令人困惑。

launch之后添加了两个coroutineScop{},其中延迟时间分别为30L和100L。

我希望看到30L延迟的日志应该在300L内部的coroutineScop{}延迟日志之前显示,但是在所有launchcoroutineScop{}

内部完成
fun testCoroutines() {
Log.e("+++", "+++ enter testCoroutines_3")
runBlocking {
    launch {
        println("+++ start Task from runBlocking, with 200L delay")
        delay(200L)
        println("+++ end Task from runBlocking, with 200L delay")
    }

    launch {
        println("+++ start Task from runBlocking, with 50L delay")
        delay(50L)
        println("+++ end Task from runBlocking, with 50L delay")
    }

    launch {
        println("+++ start Task from runBlocking, with 70L delay")
        delay(70L)
        println("+++ end Task from runBlocking, with 70L delay")
    }

    coroutineScope {
        println("+++ enter Task from coroutineScope")
        // Creates a coroutine scope
        launch {
            Log.v("+++", "+++ === start Task from nested launch, 500L")
            delay(500L)
            Log.v("+++", "+++ --- end Task from nested launch, 500L")
        }

        delay(100L)
        println("+++ in Task from coroutineScope after delay(100L)")

        launch {
            Log.v("+++", "+++ === start Task from nested launch, 300L")
            delay(300L)
            Log.v("+++", "+++ --- end Task from nested launch, 300L")
        }

        println("+++ --- exit Task from coroutine scope") // This line will be printed before the nested launch
    }

    launch {
        println("+++ start Task from runBlocking, with 30L delay")
        delay(30L)
        println("+++ end Task from runBlocking, with 30L delay")
    }

    launch {
        println("+++ start Task from runBlocking, with 100L delay")
        delay(100L)
        println("+++ end Task from runBlocking, with 100L delay")
    }

}

Log.e("+++", "--- exit  testCoroutines_3 scope is over") // This line is not printed until the nested launch completes

}

日志:

10:35:05.819 4657-4657 +++ enter testCoroutines_3
10:35:05.828 4657-4657 +++ enter Task from coroutineScope
10:35:05.833 4657-4657 +++ start Task from runBlocking, with 200L delay
10:35:05.833 4657-4657 +++ start Task from runBlocking, with 50L delay
10:35:05.833 4657-4657 +++ start Task from runBlocking, with 70L delay
10:35:05.834 4657-4657 +++ === start Task from nested launch, 500L
10:35:05.885 4657-4657 +++ end Task from runBlocking, with 50L delay
10:35:05.905 4657-4657 +++ end Task from runBlocking, with 70L delay
10:35:05.932 4657-4657 +++ in Task from coroutineScope after delay(100L)
10:35:05.933 4657-4657 +++ --- exit Task from coroutine scope
10:35:05.935 4657-4657 +++ === start Task from nested launch, 300L
10:35:06.034 4657-4657 +++ end Task from runBlocking, with 200L delay
10:35:06.235 4657-4657 +++ --- end Task from nested launch, 300L
10:35:06.334 4657-4657 +++ --- end Task from nested launch, 500L
10:35:06.335 4657-4657 +++ start Task from runBlocking, with 30L delay
10:35:06.335 4657-4657 +++ start Task from runBlocking, with 100L delay
10:35:06.366 4657-4657 +++ end Task from runBlocking, with 30L delay
10:35:06.436 4657-4657 +++ end Task from runBlocking, with 100L delay
10:35:06.437 4657-4657--- exit  testCoroutines_3 scope is over

认为至少+++ start Task from runBlocking, with 30L delay应该早于+++ === start Task from nested launch, 500L之后和+++ end Task from runBlocking, with 50L delay之前出现,但是没有,并且应该在launch完成所有+++ --- end Task from nested launch, 500L之后出现1}}。

coroutineScope在协程块中做什么?

(我正在使用Android应用程序进行测试,只需单击按钮即可调用testCoroutines

3 个答案:

答案 0 :(得分:1)

对于runBlockingcoroutineScope来说,只有在它们中启动的所有协同程序都完成后,它们才是完整的。

请注意区分“直到完成”和“直到阻塞调用线程”。 runBlocking是唯一做后者的人。

答案 1 :(得分:1)

coroutineScope是非阻塞的事实并不表示它不等待其子协程完成。

实际上,runBlockingcoroutineScope都将等待每个子协程完成后再完成。

两者之间真正的区别在于他们等待的方式。 runBlocking阻止当前线程,而coroutineScope暂停当前协程。

答案 2 :(得分:0)

link也有帮助。

enter image description here

quote:“查看fetchDocs的执行方式,您可以看到暂停的工作方式。每当协程被暂停时,都会复制当前堆栈帧(Kotlin用于跟踪正在运行的函数及其变量的位置),保存以备后用。恢复时,堆栈帧将从保存位置复制回去并再次运行。在动画的中间,当主线程上的所有协程都挂起时,主线程可以自由更新屏幕并处理用户事件。一起暂停和恢复替换回调。”