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!
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.