Python Communicating Sequential Processes — Core Concepts
What CSP actually means
Communicating Sequential Processes is a formal model for concurrency created by Tony Hoare in 1978. The core idea: programs are built from independent processes that interact exclusively through channels. Each process runs sequentially (no internal concurrency), but multiple processes run concurrently and coordinate through synchronized communication.
CSP became the theoretical foundation for Go’s goroutines and channels, Clojure’s core.async, and Rust’s crossbeam channels.
CSP vs actor model
Both avoid shared memory, but they differ in a key way:
| CSP | Actor Model |
|---|---|
| Communication through named channels | Communication through actor addresses |
| Synchronous by default (rendezvous) | Asynchronous by default (mailboxes) |
| Channels are first-class (can be passed around) | Actors are first-class entities |
| Focuses on the communication pattern | Focuses on the entities doing work |
In CSP, channels are the stars. In the actor model, actors are. This distinction affects how you design systems — CSP tends to produce pipeline-style architectures, while actors tend to produce entity-style architectures.
The rendezvous principle
The defining feature of CSP is synchronous rendezvous: a send operation blocks until a receiver is ready, and a receive blocks until a sender is ready. Both sides must meet at the channel simultaneously.
This creates a natural form of back-pressure. If a producer is faster than a consumer, the producer automatically slows down because it’s blocked waiting for the consumer. No buffer overflow, no dropped messages.
CSP in Python
Python doesn’t have Go-style channels in the standard library, but you can implement CSP patterns:
Using queue.Queue(maxsize=0) for rendezvous channels:
Setting maxsize=0 in Python’s Queue actually means unlimited, so for true CSP rendezvous, use maxsize=1 and coordinate carefully, or use multiprocessing.Pipe for paired channels.
Using third-party libraries:
- python-csp — direct CSP implementation with channels and process composition
- aiocsp — async CSP channels for asyncio
- trio — the
trio.open_memory_channel()function creates bounded channels with CSP-style semantics
Select and multiplexing
In Go, select lets a process wait on multiple channels simultaneously, responding to whichever is ready first. Python equivalents:
asyncio.wait()with multiple coroutinesselectorsmodule for I/O multiplexing- Trio’s approach with task groups and channels
This is how you build processes that can respond to multiple inputs without dedicated threads for each.
Common misconception
“CSP is just producer-consumer with queues.” Not quite. The synchronization guarantee is the difference. Regular queues buffer messages — the producer doesn’t know or care if the consumer has processed anything. CSP channels ensure both sides participate in each transfer, giving you stronger reasoning about program state.
Pipeline composition
CSP shines in pipeline architectures:
Reader → Parser → Transformer → Writer
Each stage is an independent process connected by channels. You can add stages, remove them, or swap implementations without touching other parts. The channel contracts are the API.
The one thing to remember: CSP gives you concurrency through synchronized channels rather than shared memory. The rendezvous model means communication is a coordination point, not just data transfer — making your concurrent programs easier to reason about.
See Also
- Python Actor Model Why treating each piece of your program like a person with their own mailbox makes concurrency way less scary.
- Python Aiocache Caching aiocache remembers expensive answers so your async Python app doesn't waste time asking the same question twice.
- Python Aiofiles Async Io aiofiles lets your async Python program read and write files without freezing — because normal file operations secretly block everything.
- Python Aiohttp Understand Aiohttp through an everyday analogy so Python behavior feels intuitive, not random.
- Python Anyio Portability AnyIO lets your async Python code work with any async library — write once, run on asyncio or Trio without changes.