Bird
Raised Fist0
Kotlinprogramming~10 mins

Coroutine scope and structured concurrency in Kotlin - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Coroutine scope and structured concurrency
Start Coroutine Scope
↓
Launch Child Coroutines
↓
Child Coroutines Run Concurrently
↓
Wait for All Children to Complete
↓
Coroutine Scope Ends
↓
Continue with Rest of Program
This flow shows how a coroutine scope launches child coroutines, waits for all to finish, then ends, ensuring structured concurrency.
Execution Sample
Kotlin
fun main() = runBlocking {
  launch { delay(100); println("First") }
  launch { delay(50); println("Second") }
  println("Start")
}
This code runs two child coroutines inside a scope, printing messages with delays, showing structured concurrency.
Execution Table
StepActionCoroutineDelay (ms)OutputState
1Enter runBlocking scopemain0Scope started, waiting for children
2Launch first coroutinefirst100First coroutine started, waiting 100ms
3Launch second coroutinesecond50Second coroutine started, waiting 50ms
4Print in main coroutinemain0StartPrinted 'Start' immediately
5Second coroutine delay endssecond50SecondPrinted 'Second' after 50ms
6First coroutine delay endsfirst100FirstPrinted 'First' after 100ms
7All child coroutines completemain0Scope ends, main continues
8Program endsmain0runBlocking returns, program finishes
💡 All child coroutines finished, so runBlocking scope ends and program continues.
Variable Tracker
VariableStartAfter Step 2After Step 3After Step 5After Step 6Final
Scope Activefalsetruetruetruetruefalse
First Coroutine Statenot startedstartedstartedstartedcompletedcompleted
Second Coroutine Statenot startednot startedstartedcompletedcompletedcompleted
Output PrintedStartStart, SecondStart, Second, FirstStart, Second, First
Key Moments - 3 Insights
Why does the program wait for both child coroutines before finishing?
Because runBlocking creates a coroutine scope that waits for all launched child coroutines to complete before it ends, as shown in steps 7 and 8.
Why is "Start" printed before "Second" and "First"?
"Start" is printed immediately in the main coroutine without delay (step 4), while the child coroutines print after their delays (steps 5 and 6).
What happens if a child coroutine throws an error inside the scope?
The coroutine scope cancels all other child coroutines and the error propagates, ensuring structured concurrency and no orphan coroutines.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is printed at step 5?
A"Second"
B"First"
C"Start"
DNothing
💡 Hint
Check the 'Output' column at step 5 in the execution_table.
At which step does the coroutine scope end?
AStep 6
BStep 4
CStep 7
DStep 8
💡 Hint
Look for the step where 'Scope ends' is mentioned in the 'State' column.
If the delay in the first coroutine was changed to 30ms, when would "First" print relative to "Second"?
A"First" and "Second" would print at the same time
B"First" would print before "Second"
C"Second" would still print before "First"
D"First" would never print
💡 Hint
Compare delays in the 'Delay (ms)' column and their effect on output timing.
Concept Snapshot
Coroutine scope creates a block where child coroutines run.
All child coroutines must finish before the scope ends.
runBlocking is a coroutine scope that blocks the thread until done.
Structured concurrency prevents orphan coroutines and manages errors.
Use launch inside a scope to start child coroutines.
Delays simulate work and show concurrency timing.
Full Transcript
This example shows how a coroutine scope manages child coroutines. The main function uses runBlocking to create a scope. Inside, two child coroutines launch with different delays. The main coroutine prints "Start" immediately. The second coroutine prints "Second" after 50 milliseconds. The first coroutine prints "First" after 100 milliseconds. The runBlocking scope waits for both child coroutines to finish before ending. This ensures structured concurrency, meaning no child coroutine is left running outside the scope. If any child coroutine fails, the scope cancels others to keep the program safe. This way, coroutines are managed cleanly and predictably.

Practice

(1/5)
1. What is the main purpose of a CoroutineScope in Kotlin?
easy
A. To block the main thread until coroutines finish
B. To create threads manually for parallel execution
C. To group coroutines and manage their lifecycle together
D. To convert coroutines into synchronous functions

Solution

  1. Step 1: Understand CoroutineScope role

    A CoroutineScope groups coroutines so they can be managed as one unit.
  2. Step 2: Compare other options

    The other options describe unrelated or incorrect behaviors for CoroutineScope.
  3. Final Answer:

    To group coroutines and manage their lifecycle together -> Option C
  4. Quick Check:

    CoroutineScope groups coroutines [OK]
Hint: CoroutineScope manages coroutines lifecycle as a group [OK]
Common Mistakes:
  • Thinking CoroutineScope creates threads
  • Confusing CoroutineScope with blocking calls
  • Assuming CoroutineScope converts coroutines to sync
2. Which of the following is the correct way to launch a coroutine within a CoroutineScope?
easy
A. scope.launch { /* coroutine code */ }
B. launch.scope { /* coroutine code */ }
C. CoroutineScope.launch { /* coroutine code */ }
D. scope.run { /* coroutine code */ }

Solution

  1. Step 1: Recall coroutine launch syntax

    Within a CoroutineScope, you call launch as a function on the scope instance.
  2. Step 2: Check options for syntax correctness

    scope.launch { /* coroutine code */ } uses scope.launch {} which is correct. Others misuse syntax or functions.
  3. Final Answer:

    scope.launch { /* coroutine code */ } -> Option A
  4. Quick Check:

    launch is called on CoroutineScope instance [OK]
Hint: Use scope.launch { } to start coroutine inside scope [OK]
Common Mistakes:
  • Swapping order like launch.scope
  • Using run instead of launch
  • Calling launch as a static method
3. What will be the output of this Kotlin code snippet?
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        delay(100)
        println("Task done")
    }
    println("Waiting")
    job.join()
    println("Finished")
}
medium
A. Task done\nWaiting\nFinished
B. Waiting\nTask done\nFinished
C. Waiting\nFinished\nTask done
D. Finished\nWaiting\nTask done

Solution

  1. Step 1: Analyze coroutine launch and delay

    The coroutine launched delays 100ms before printing "Task done". Meanwhile, "Waiting" prints immediately.
  2. Step 2: Understand job.join() effect

    The main coroutine waits for the launched job to finish before printing "Finished".
  3. Final Answer:

    Waiting\nTask done\nFinished -> Option B
  4. Quick Check:

    Output order respects delay and join [OK]
Hint: join() waits for coroutine to finish before continuing [OK]
Common Mistakes:
  • Assuming launched coroutine runs instantly
  • Ignoring delay effect
  • Printing Finished before job completes
4. Identify the problem in this Kotlin coroutine code and choose the correct fix:
fun main() = runBlocking {
    val job = GlobalScope.launch {
        delay(1000)
        println("Done")
    }
    println("Waiting")
}
medium
A. Replace delay(1000) with Thread.sleep(1000)
B. Use CoroutineScope().launch {} instead of GlobalScope.launch
C. Remove runBlocking and use GlobalScope.launch only
D. Add job.join() after println("Waiting")

Solution

  1. Step 1: Identify lifecycle issue with GlobalScope

    Using GlobalScope.launch starts coroutine that may outlive runBlocking, so "Done" may never print.
  2. Step 2: Fix by waiting for job completion

    Calling job.join() ensures main coroutine waits for launched coroutine to finish before exiting.
  3. Final Answer:

    Add job.join() after println("Waiting") -> Option D
  4. Quick Check:

    join() waits for coroutine to complete [OK]
Hint: Always join launched jobs to wait for completion [OK]
Common Mistakes:
  • Ignoring job.join() causing premature exit
  • Replacing delay with blocking sleep
  • Removing runBlocking causing main to exit early
5. Given this Kotlin code using structured concurrency, what will be printed?
import kotlinx.coroutines.*

fun main() = runBlocking {
    try {
        coroutineScope {
            launch {
                delay(100)
                println("Child 1 done")
            }
            launch {
                delay(50)
                throw RuntimeException("Error in Child 2")
            }
        }
    } catch (e: Exception) {
        println("Caught: " + e.message)
    }
    println("End of main")
}
hard
A. Caught: Error in Child 2\nEnd of main
B. Child 1 done\nCaught: Error in Child 2\nEnd of main
C. Caught: Error in Child 2
D. Child 1 done\nEnd of main

Solution

  1. Step 1: Understand coroutineScope behavior on exceptions

    If any child coroutine throws an exception, coroutineScope cancels all children and throws the exception.
  2. Step 2: Analyze timing and cancellation

    Child 2 throws after 50ms, cancelling Child 1 before it prints. The exception is caught and message printed.
  3. Final Answer:

    Caught: Error in Child 2\nEnd of main -> Option A
  4. Quick Check:

    Exception cancels siblings, caught prints message [OK]
Hint: Exception cancels sibling coroutines in coroutineScope [OK]
Common Mistakes:
  • Assuming Child 1 prints despite cancellation
  • Missing try-catch around coroutineScope
  • Thinking exceptions are ignored in coroutineScope
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard