在runBlocking中使用coroutineScope有什么效果?

what is the effect using coroutineScope in the runBlocking?

在kotlin协程doc中,解释了“runBlocking和coroutineScope的区别”:

Scope builder

In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using coroutineScope builder. It creates a coroutine scope and does not complete until all launched children complete. 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 延迟。

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

这里在coroutineScop{}后面加了两个launch,分别有30L和100L的延迟。

我期待看到 30L 延迟的日志应该出现在 coroutineScop{} 内的 300L 延迟之前,但它毕竟出现了 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.

coroutineScope 在协程块中做了什么?

(我正在使用 android 应用进行测试,点击按钮即可调用 testCoroutines

对于 runBlockingcoroutineScope 都是如此,它们只有在其中启动的所有协程都完成后才会完成。

注意区分"not complete until"和"block the calling thread until"。 runBlocking 是唯一一个做后者的。

coroutineScope 是 non-blocking 的事实并不意味着它不等待其子协程完成。

事实上,runBlockingcoroutineScope都会等待每个子协程完成后才完成。

两者之间的真正区别在于他们如何等待。 runBlocking 阻塞当前线程,而 coroutineScope 挂起当前协程。

这个link也有帮助。

引用:"Looking at how fetchDocs executes, you can see how suspend works. Whenever a coroutine is suspended, the current stack frame (the place that Kotlin uses to keep track of which function is running and its variables) is copied and saved for later. When it resumes, the stack frame is copied back from where it was saved and starts running again. In the middle of the animation — when all of the coroutines on the main thread are suspended, the main thread is free to update the screen and handle user events. Together, suspend and resume replace callbacks."