Kotlin: Difference between calling CoroutineScope.launch vs launch inside a coroutine

378 views Asked by At

I am trying to understand structured concurrency in Kotlin and I am unable to wrap my head across this piece of code.

fun main(): Unit = runBlocking {
    other(this)
}

suspend fun other(scope: CoroutineScope) {
    val job = scope.launch {
        scope.launch {
            delay(200)
            println("e")
        }
        println("a")
    }
    job.invokeOnCompletion {
        println("Complete")
    }
}

The code prints

a
Complete
e

While if I replace the inner scope.launch call with launch, like this

suspend fun other(scope: CoroutineScope) {
    val job = scope.launch {
       launch {
            delay(200)
            println("e")
        }
        println("a")
    }
    job.invokeOnCompletion {
        println("Complete")
    }
}

It prints

a
e
Complete

This shows that the first example does not follow structured concurrency since parent job finished before child job. My confusion is, why does this happen?

I felt that scope.launch maybe equivalent to calling launch (which should be equivalent to this.launch and this refers to scope) in this case. But seems like this is not true. Can someone explains why the first one results in unstructured concurrency and what is the difference between the two launch calls? Thanks!

1

There are 1 answers

1
ostrich_network On BEST ANSWER

In the first code, while the inner launch looks like it's a child of the outer launch, it's actually not -- it's a sibling of the outer launch since they were both launched from the same scope. So waiting for the outer launch's job to complete doesn't wait for the inner one.

The second code uses structured concurrency since the inner launch uses the scope created by the outer launch (the receiver of the launch block). In this case it's a child of the outer launch so waiting for the outer job to complete waits for the child to complete as well.

The second one is what you're supposed to do: use the CoroutineScope receiver of the launch block to launch child jobs. Using some other scope instead does not provide structured concurrency.