liveBook · Manning

Concurrency

As explained in Code Like a Pro in Rust

Concurrency is the ability for multiple computations to make progress during overlapping periods, whether or not they execute simultaneously.

Concurrency lets multiple tasks make progress during overlapping periods; it does not require them to execute simultaneously. In async Rust, a task waiting for I/O can make room for other work. This is especially useful when waiting takes much longer than processing the result. section 8.0 section 8.2

Choose how tasks overlap:

  • Use `tokio::join!` or `futures::future::join_all()` to drive multiple futures concurrently. Use `tokio::select!` to wait on multiple concurrent branches. section 8.5
  • Use `tokio::task::spawn()` to let Tokio schedule a task on a worker thread. It permits parallel execution but does not guarantee it; a single-worker runtime still runs its tasks on one thread. section 8.5
Test 1: Run 2 async tasks sequentially
Entering sleep_1s_blocking(Task 1)
Returning from sleep_1s_blocking(Task 1)
Entering sleep_1s_blocking(Task 2)
Returning from sleep_1s_blocking(Task 2)
Test 2: Run 2 async tasks concurrently (same thread)
Entering sleep_1s_blocking(Task 3)
Returning from sleep_1s_blocking(Task 3)
Entering sleep_1s_blocking(Task 4)
Returning from sleep_1s_blocking(Task 4)
Test 3: Run 2 async tasks in parallel
Entering sleep_1s_blocking(Task 5)         #1
Entering sleep_1s_blocking(Task 6)
Returning from sleep_1s_blocking(Task 5)
Returning from sleep_1s_blocking(Task 6)

Compare the printed order: with blocking sleep, joined tasks finish one after the other, while spawned tasks can enter before either returns. section 8.5

async fn sleep_1s_nonblocking(task: &str) {
   use tokio::time::{sleep, Duration};
   println!("Entering sleep_1s_nonblocking({task})");
   sleep(Duration::from_secs(1)).await;
   println!("Returning from sleep_1s_nonblocking({task})");
}

Replace blocking `thread::sleep` with `tokio::time::sleep(...).await` so waiting does not prevent another joined task from progressing. section 8.5

Use async when concurrency is needed, particularly for many I/O operations or network requests. Prefer simpler synchronous code for a CLI doing sequential file I/O or a client making only a few sequential requests. Plan ahead: adding async later is harder, while Tokio adds some overhead to otherwise sequential work. section 8.8

How Code Like a Pro in Rust covers it

How deep it goes
Demonstrates Tokio task composition and blocking versus nonblocking sleep, while leaving individual task-to-thread scheduling to the runtime.
Examples
Compares sequential awaits, joined tasks, and spawned tasks using blocking and nonblocking one-second sleeps and their printed order.
What it assumes
Assumes familiarity with Rust code and introduces async concurrency through Tokio calls and task-order comparisons.
What you can do after
Apply it via Tokio joins, selection, and task spawning while choosing async only when the workload needs concurrency.
How it connects
Connects concurrency to asynchronous programming, futures, nonblocking I/O, parallelism, and worker threads.

Also explained in

Compare how 23 books explain concurrency →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Code Like a Pro in Rust. The section links point to the parts of the book it draws on.