Course contentsShow
← Rust
Lesson 1107 of 1,91010. Concurrency PrimitivesFree lesson

Philosophies of Communicating Sequential Processes

You will adopt the 'share memory by communicating' paradigm to decouple thread logic.

In this lesson, you will learn how to decouple your thread logic by shifting from "locking shared state" to the "Communicating Sequential Processes" (CSP) philosophy.

Moving Beyond the Lock

Up until now, our concurrency model has been heavily focused on shared memory: we wrap data in Arc<Mutex<T>> and fight to ensure that threads don't step on each other’s toes. While safe, this creates a tight coupling where the thread's logic is inextricably linked to the lock’s acquisition order and the shared state’s lifecycle.

The CSP philosophy—famously summarized by Tony Hoare—suggests a different path: "Do not communicate by sharing memory; instead, share memory by communicating."

The Shift in Logic

In a CSP-based architecture, you view your program as a collection of independent, sequential processes (or threads) that own their own data exclusively. When a thread needs to pass information to another, it doesn't modify a shared variable; it sends a discrete "message" over a channel.

Think of it like a production line. Instead of multiple people grabbing the same toolbox in the middle of the room, each worker has their own tools. When they finish a part, they place it on a conveyor belt for the next worker. The worker at the end of the belt receives the item, processes it, and never needs to know how the previous worker managed their tools.

A Concrete Illustration

Consider how this cleans up your thread logic:

use std::sync::mpsc; // Multi-producer, single-consumer channels
use std::thread;

// Each thread owns its data and logic; no Mutex needed.
let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    let my_private_data = vec![1, 2, 3];
    // We send the ownership of the data through the channel.
    tx.send(my_private_data).unwrap();
});

// The receiver takes ownership of the data sent by the sender.
let received_data = rx.recv().unwrap();
println!("Received: {:?}", received_data);

By passing data through a channel, the ownership moves with the message. This eliminates the need for Mutex guards entirely, as there is no state being accessed concurrently by multiple threads. Each process remains sequential, local, and infinitely easier to reason about.

Key Takeaway: Instead of forcing threads to synchronize access to shared variables via locks, use channels to pass ownership of data, allowing threads to act as independent, isolated units of logic.