liveBook · Manning

Chapter 10: Processes, threads, and containers

What you will learn in chapter 10 of Rust in Action: Systems programming concepts and techniques

The introduction distinguishes concurrency from parallelism and explains how a single CPU core can support concurrent tasks.

After this chapter you will be able to

What it builds on

Section by section

Introduction

The introduction distinguishes concurrency from parallelism and explains how a single CPU core can support concurrent tasks.

Key terms: processes, threads, containers, concurrency, parallelism, single CPU core, blocked state, rescheduled, memory, I/O

10.1 Anonymous functions

Introduces Rust lambda functions as locally defined closures and compares one with a regular function.

Key terms: lambda functions, closures, vertical bars, global scope, main(), regular function, lambda function

10.2 Spawning threads

Introduces threads as the operating system’s main way to enable concurrent execution and explains why spawning them matters for using multi-core CPUs.

Key terms: threads, concurrent execution, spawning, CPU, multi-core CPUs

10.2.1 Introduction to closures

This section introduces the anonymous functions passed to thread::spawn and explains why captured values often need to move into a spawned thread’s closure.

Key terms: anonymous function, std::thread::spawn(), capture, move, ownership, closure, calling scope

10.2.2 Spawning a thread

The section uses a sleeping subthread to explain what joining a spawned thread means.

Key terms: spawned, forked, join, join(), calling thread

10.2.3 Effect of spawning a few threads

This section uses a two-thread timing example to show that spawning a few threads has little measured overhead, while explaining why large numbers of threads can slow down.

Key terms: spawning a thread, negligible impact, benchmark suite, threads don’t scale, OS scheduler, memory

10.2.4 Effect of spawning many threads

This section uses sleep and spin-loop measurements to show the costs of spawning many threads, the limits of timing accuracy, and the poor scaling of CPU-intensive threads beyond the number of physical cores.

Key terms: spawning threads, switching between threads, caches, spin loop, sleep, CPU-intensive multithreading, physical cores, hyper-threading

10.2.5 Reproducing the results

The section explains the waiting strategies used to generate thread measurements, how to join stored thread handles, and what yielding does during a busy wait.

Key terms: sleep, busy wait, busy loop, spin loop, join(), handlers, pop(), ownership, reference, std::thread::yield_now()

10.2.6 Shared variables

This subsection shows that adding `move` to closures fixes an attempted use of a parent-scope variable in multiple threads, relying on `Copy`.

Key terms: move keyword, closures, move semantics, Copy

10.3 Differences between closures and functions

This section explains why Rust closures and functions are not always interchangeable by contrasting their representations and the special case of closures that capture no variables.

Key terms: closure, anonymous struct, std::ops::FnOnce, std::ops::Fn, std::ops::FnMut, environment, function pointer, code

10.4 Procedurally generated avatars from a multithreaded parser and code generator

Introduces an application that generates distinctive default avatars from usernames using a small drawing language.

Key terms: digest, hash function, procedural generation, parallax lines, Base 16 alphabet, opcodes

10.4.1 How to run render-hex and its intended output

The subsection describes render-hex’s intended image output and how to view it.

Key terms: render-hex, Base 16 bytes, SVG

10.4.2 Single-threaded render-hex overview

This section explains how the single-threaded render-hex program turns hexadecimal input into an SVG drawing through a staged pipeline.

Key terms: render-hex, STDIN, pipeline, main(), SVG, Operation enum, virtual pen, Artist, heading, convert()

10.4.3 Spawning a thread per logical task

The section refactors render-hex’s parse() function to use iterators, then shows how Rayon can process its data in parallel.

Key terms: map(), collect(), Vec<T>, closure, iterators, rayon, data parallelism, prelude, par_iter(), par_bytes()

10.4.4 Using a thread pool and task queue

This section explains how channels can connect task producers to worker threads and applies a channel-based task queue to render-hex.

Key terms: task queue, worker threads, Arc<Mutex<T>>, Mutex, Arc, channel, Sender<T>, Receiver<T>, send(), recv()

10.5 Concurrency and task virtualization

Introduces concurrency terminology and explains how stronger task isolation trades greater protection for greater overhead.

Key terms: isolation, task virtualization, runtime overhead, program, process, thread of execution, coroutine, task, concurrency, non-blocking I/O

10.5.1 Threads

This subsection distinguishes OS-scheduled threads from smaller forms of concurrency whose task switching a process manages itself.

Key terms: thread, coroutines, fibers, green threads, context switching

10.5.2 What is a context switch?

This section explains what a context switch is, why it has a cost, and why CPUs switch tasks while programs wait for data.

Key terms: context switch, virtualization, isolation, CPU registers, CPU caches, tasks, CPU, memory, disk, network

10.5.3 Processes

This subsection explains how processes isolate memory and why sharing data between them requires operating-system support.

Key terms: threads, process, OS, CPU, Rust channels, Arc<Mutex<_>>, network sockets, interprocess communication (IPC)

10.5.4 WebAssembly

This section explains how WebAssembly isolates tasks within a process by giving modules linear memory rather than direct access to the process’s addresses.

Key terms: WebAssembly, Wasm, process boundary, tasks, linear memory, runtime, virtual memory, address space

10.5.5 Containers

This section distinguishes containers from processes by the additional OS-provided isolation of resources such as filesystems and networks.

Key terms: containers, processes, filesystem, network, namespaces, address space

10.5.6 Why use an operating system (OS) at all?

This section explains what it means to run an application without an operating system, what support that gives up, and how a unikernel offers a middle ground.

Key terms: freestanding, freestanding binaries, virtual memory, multithreading, unikernel

10.6 Summary

Where this is used later

Concepts explained in this chapter

← Chapter 9: Time and timekeeping Chapter 11: Kernel →

These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.