Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is the effect using coroutineScope in the runBlocking?

in kotlin coroutines doc, it explains the "difference between runBlocking and 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.

I dont quite understand, the code of the sample shows

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
}

the log:

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{}

and the +++ Task from nested launch is show after +++ Task from runBlocking as expected, since it has 500L delay.

But if adding some some other launch builders after the coroutineScop{} block, the result is confusing.

Here two launch are added after the coroutineScop{} with 30L and 100L delay in them.

I was expecting to see the log for the 30L delay should show up before the 300L delay ones inside the coroutineScop{}, but it shows up after all launch are done inside the coroutineScop{}

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

}

the log:

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

thought at least the +++ start Task from runBlocking, with 30L delay should show up earlier at after the +++ === start Task from nested launch, 500L and before +++ end Task from runBlocking, with 50L delay, but it doesnt and shows up after all launch are done at +++ --- end Task from nested launch, 500L.

What does the coroutineScope do in the coroutines block?

(I was testing using android app with a button click to call the testCoroutines)

like image 361
lannyf Avatar asked May 16 '19 15:05

lannyf


1 Answers

The fact that coroutineScope is non-blocking does not imply that it doesn't wait for its child coroutines to finish.

In fact, both runBlocking and coroutineScope will wait for every child coroutine to finish before completing.

The real difference between the 2 is how they wait. runBlocking blocks the current thread, while coroutineScope suspends the current coroutine.

like image 185
Joffrey Avatar answered Oct 13 '22 11:10

Joffrey