22.1 Concurrency, Processes, and Threads
22.1.1 Concurrency
Concurrency is the ability to manage multiple tasks that can overlap in time. On single-core CPUs, an operating system can switch tasks so quickly that they appear simultaneous. On multi-core systems, concurrency may become true parallelism when tasks run on different cores at the same time.
Common concurrency pitfalls include:
- Deadlocks: Threads block each other because each holds a resource the other needs, causing a freeze or stall.
- Race Conditions: The result of operations varies unpredictably based on the timing of reads and writes to shared data.
In C or C++, these bugs often manifest at runtime as elusive, intermittent crashes or undefined behavior. In Rust, many concurrency problems are caught at compile time through ownership and borrowing rules. Rust simply won’t compile code that attempts unsynchronized mutations from multiple threads.
22.1.2 Processes and Threads
It’s important to distinguish processes from threads:
- Processes: Each has its own address space, communicating with other processes through sockets, pipes, shared memory, or similar IPC mechanisms. Processes are generally well-isolated.
- Threads: Multiple threads within a single process share the same address space. This makes data sharing easier but increases the risk of data races if not carefully managed.
Rust’s concurrency primitives make threading safer. Tools like Mutex<T>
, RwLock<T>
, and Arc<T>
work with the language’s type system to ensure proper synchronization and help prevent race conditions.