liveBook · Manning

Concurrency

As explained in Rust in Action: Systems programming concepts and techniques

Concurrency is the execution of multiple computational activities during overlapping time periods, potentially on the same or different processors.

Concurrency lets a program carry out multiple tasks over overlapping periods. It does not require multiple CPU cores: on one core, the operating system can block a thread waiting for memory or I/O and schedule it again when the data is available. Parallelism is the related use of multiple physical cores at the same time. section 10.0

Rust’s promise of “fearless concurrency” rests on its emphasis on safety across independent threads. Threads can otherwise interfere with one another when a programmer makes a serious mistake. Rust has no global interpreter lock limiting a thread’s speed, but safety does not remove the need to choose how tasks run and communicate. section 1.7.2 section 1.12

Listing 1.4 Example of Rust preventing a race condition
 use std::thread;                           #1
 fn main() {
     let mut data = 100;
 
     thread::spawn(|| { data = 500; });     #2
     thread::spawn(|| { data = 1000; });    #2
     println!("{}", data);
 }

Two spawned threads attempt to change the same local variable. The example is rejected rather than allowed to become a race condition. section 1.6.1

The unit used to carry out a task affects both isolation and cost:

  • Threads in one process share an address space; processes cannot inspect one another’s memory. section 10.5
  • Greater isolation can prevent some failures from spreading, but generally adds runtime overhead. Isolation is not absolute: tasks still need input and output. section 10.5
  • Coroutines within a blocked thread cannot progress while that thread waits for console output. section 10.5

Too many CPU-intensive threads

A comparison of sleeping threads and threads running spin loops shows why spawning more threads is not automatically faster. On the six-core CPU used for the measurement, CPU-intensive performance degraded quickly once the thread count exceeded the physical core count. section 10.2.4

Figure 10.3 Comparing the time taken to wait for 20m using the sleep strategy (circles) versus the spin lock strategy (plus symbols). This chart shows the differences that occur as the number of threads exceeds the number of CPU cores (6).

The chart makes the change at six physical cores visible when competing threads outnumber cores. section 10.2.4

Closures provide syntax used by Rust’s concurrency APIs. From there, practical tools include spawning threads, channels and message passing, task queues, and the standard library alongside the crossbeam and rayon crates. Using these tools is a different goal from implementing a concurrency crate. section 10.0

How Rust in Action: Systems programming concepts and techniques covers it

How deep it goes
Explains scheduling and isolation trade-offs and introduces concurrency tools, but stops short of implementing concurrency crates.
Examples
Uses a rejected thread-spawning race example and measured sleep-versus-spin-loop workloads on a six-core CPU.
What it assumes
Introduces closures and thread spawning before relying on them, without assuming prior distinctions among processes, threads, and containers.
What you can do after
Apply it via Rust’s standard library, crossbeam, and rayon while choosing among available concurrency tools.
How it connects
Connects concurrency to parallelism, threads, processes, closures, channels, message passing, and task queues.

Also explained in

Compare how 23 books explain concurrency →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Rust in Action: Systems programming concepts and techniques. The section links point to the parts of the book it draws on.