← Back to Kotlin Course | Chapter 12: Coroutines | Lesson 7 of 7

Structured Concurrency

Structured concurrency means every background task is tied to a parent scope, so nothing gets left running by accident once its parent is done.

Parents Wait for Their Children

A coroutine scope, such as the one created by runBlocking, does not complete until every coroutine launched inside it has also finished, tying child task lifetimes to their parent.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: Parents Wait for Their Children

markup
import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(50)
        println("Child coroutine finished")
    }
    println("Parent reached its last line, but will wait for the child")
}

coroutineScope Groups Related Work

coroutineScope { } creates a new scope for launching coroutines that is tied to the calling coroutine's context, and it suspends until every coroutine started inside it completes.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: coroutineScope Groups Related Work

markup
import kotlinx.coroutines.*

suspend fun loadData() = coroutineScope {
    launch {
        delay(30)
        println("Loaded part 1")
    }
    launch {
        delay(30)
        println("Loaded part 2")
    }
}

fun main() = runBlocking {
    loadData()
    println("All data loaded")
}

Cancellation Propagates to Children

Cancelling a parent job automatically cancels every child coroutine launched under it, ensuring related work doesn't keep running pointlessly after its parent has stopped caring about the result.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: Cancellation Propagates to Children

markup
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        launch {
            delay(1000)
            println("This should not print")
        }
        delay(20)
    }
    delay(50)
    job.cancelAndJoin()
    println("Parent and child both cancelled")
}

Why Structured Concurrency Matters

By tying every coroutine's lifetime to a well-defined scope, structured concurrency avoids the classic problem of background tasks silently outliving the operation that started them, which is a common source of subtle bugs in unstructured async code.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: Why Structured Concurrency Matters

markup
import kotlinx.coroutines.*

suspend fun fetchBoth(): Pair<Int, Int> = coroutineScope {
    val a = async { delay(20); 10 }
    val b = async { delay(20); 20 }
    Pair(a.await(), b.await())
}

fun main() = runBlocking {
    val (x, y) = fetchBoth()
    println("x=$x, y=$y")
}
Common Mistakes
  1. Launching coroutines from a global, unscoped context (like GlobalScope) out of habit, losing the automatic cancellation and lifecycle management structured concurrency provides.
  2. Assuming a parent coroutine finishes as soon as its own body's last line runs, forgetting it actually waits for all its child coroutines to complete first.
  3. Not handling CancellationException specially when catching exceptions broadly, which can accidentally swallow legitimate coroutine cancellation.
Chapter Summary
  • In structured concurrency, every coroutine is launched within a scope, and a parent coroutine does not complete until all its children do.
  • Cancelling a parent coroutine automatically cancels all of its child coroutines.
  • coroutineScope { } creates a new scope tied to its calling context, waiting for all coroutines launched inside it to finish.
  • Structured concurrency prevents leaked coroutines that keep running unexpectedly after their logical parent has already finished.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.