Search code examples
androidkotlinkotlin-coroutinescoroutinescope

Kotlin execution order mix with different scope and coroutines


I'm trying to figure out the order of the execution of coroutine. Here is my example.

fun theCoroutineOrderWithDifferentScope() = runBlocking {
    coroutineScope {
        println("in the coroutineScope 1 ")
        coroutineScope {
            println("coroutineScope inside the coroutineScope1")
        }
    }

    coroutineScope {
        launch {
            println("coroutine in the coroutineScope 2")
            coroutineScope {
                println("coroutineScope inside the coroutineScope2 coroutine")
            }
        }
        println("in the coroutineScope 2")
    }

    launch { // #1
        println("coroutine in the blocking")
    }

    println("in the blocking")
}

I got this result

result A

in the coroutineScope 1 
coroutineScope inside the coroutineScope1
in the coroutineScope 2
coroutine in the coroutineScope 2
coroutineScope inside the coroutineScope2 coroutine
in the blocking
coroutine in the blocking

So I conclude that it will fist execute the child scope line by line from top to down, after finish it, will start execute the parent coroutine then the child coroutine.

But after I change the order of the code block(#1) the order of the result change

fun theCoroutineOrderWithDifferentScope() = runBlocking {
    coroutineScope {
        println("in the coroutineScope 1 ")
        coroutineScope {
            println("coroutineScope inside the coroutineScope1")
        }
    }

    launch { // #1
        println("coroutine in the blocking")
    }

    coroutineScope {
        launch {
            println("coroutine in the coroutineScope 2")
            coroutineScope {
                println("coroutineScope inside the coroutineScope2 coroutine")
            }
        }
        println("in the coroutineScope 2")
    }

    println("in the blocking")
}

the result order become like this

result B

in the coroutineScope 1 
coroutineScope inside the coroutineScope1
in the coroutineScope 2
coroutine in the blocking
coroutine in the coroutineScope 2
coroutineScope inside the coroutineScope2 coroutine
in the blocking

Why the result is not as same as result A??

I have tried search the previous questions on SOF


Solution

  • The behavior of coroutineScope is:

    • Immediately executes the lambda.
    • Suspends the current thread after the lambda finishes.
    • Blocks further execution until the lambda finishes and all coroutines launched therein are complete.

    The behavior of launch is:

    • Schedules the execution of the lambda for a later time. By default that is as soon as possible, but that can be customized by setting the start parameter.
      • Even when it should be started as soon as possible the coroutine must either be moved to another thread (which takes some time) to run in parallel or it must wait for the current thread to suspend to simply run asynchronously.
    • Doesn't block further execution and immediately returns.

    The nested coroutineScopes don't launch anything so they can be removed without affecting the output, making everything a bit simpler.


    Your example code only ever runs on a single thread, but some parts of your code are executed asynchronously:

    For result A that means that the first coroutine is executed when the current thread is free. That happens before the second coroutineScope completes but after its lambda is finished. The current thread is suspended after println("in the coroutineScope 2"), and only then the coroutine can start.

    The second coroutine is launched before println("in the blocking"), but it only starts executing when the current thread suspends, which is after the runBlocking lambda finishes (but before runBlocking itself returns).

    For result B the first coroutine is launched between the two coroutineScopes, but as always it is only executed when the current thread suspends. That will be some time later, but first let's see what happens after the first coroutine is launched: The second coroutineScope is entered and the second coroutine is launched as well. We now have two scheduled coroutines waiting for execution.

    The next window arrives when the second coroutineScope suspends after the execution of its lambda, right after println("in the coroutineScope 2") but still before the coroutineScope itself returns. Now all scheduled coroutines are executed. We only have a single thread so this is done sequentially: The first coroutine first, and after it completes the second coroutine is executed.

    Only when that also completes the control is given back to the second coroutineScope which determines that it is done (it needed to wait for the second coroutine to finish) and only then println("in the blocking") is executed.