Skip to content

Structured concurrency

Category: Async · Status: stub · Lessons: chapter 06, Async (planned)

One line: Concurrent tasks are started inside a scope that does not end until all of them have, so no task outlives the code that started it and every error reaches that code.

Also called: nursery, task group, scoped threads.

How it connects

flowchart LR
  n_cancellation["Cancellation"]
  n_join["Join"]
  n_task_leak["Leaked tasks"]
  n_structured_concurrency["Structured concurrency"]
  n_structured_concurrency -->|prevents| n_task_leak
  n_structured_concurrency -->|uses| n_cancellation
  n_structured_concurrency -->|uses| n_join
  classDef center stroke-width:3px
  class n_structured_concurrency center
  classDef outside stroke-dasharray: 4 3
  class n_cancellation,n_join,n_task_leak outside

In each language

Rust thread::scope: every thread spawned in the scope is joined before it returns, which is why scoped threads may borrow local data
Go not in std; errgroup waits for a group of goroutines and cancels their context the first time one returns an error
C++ no scope construct; a std::jthread (C++20) joins on destruction, where a still-joinable std::thread calls std::terminate
Java StructuredTaskScope, a preview API in JDK 25 (JEP 505 ↗, fifth preview) and again in JDK 26 (JEP 525 ↗, sixth preview)
Python asyncio.TaskGroup, added in 3.11
Kotlin the default: coroutines start inside a CoroutineScope, and coroutineScope returns only when its block and all coroutines launched in it have completed
Swift task groups and async let create child tasks; the Swift book ↗ calls that explicit parent-child relationship structured concurrency
Elsewhere Trio's nurseries ↗, in Python

Where to read more