9/32 · 5 min
Choosing between the two
Both give concurrency. They differ in what is shared, and everything else follows from that.
What is shared
| Processes | Threads | |
|---|---|---|
| Memory | separate | shared |
| Open files | copied at fork | shared |
| Creation cost | higher | lower |
| Switch cost | higher, TLB flushed | lower |
| One crashes | others survive | usually the whole process dies |
| Communication | needs a mechanism | a variable |
| Data races possible | no | yes |
The last two rows are the same fact seen twice. Shared memory makes communication free and correctness hard.
When processes are right
Isolation matters. A browser puts each tab in its own process so a crash in one does not take the window with it, and so a compromised page cannot read another site's memory.
Fault tolerance. A supervisor restarting a crashed worker is only possible if the crash was contained.
Python, specifically. The global interpreter lock means threads do not run Python bytecode in parallel, so multiprocessing is the answer for CPU bound work and threading only helps when the work is I/O bound.
When threads are right
Shared state is the point. An in memory cache that every request reads is natural with threads and awkward across processes.
Many short lived units of work. Creating a thread is far cheaper than a process.
The third option
Neither, for I/O bound work. A single thread with an event loop handles thousands of concurrent connections, because each one is mostly waiting, and waiting does not need a thread of its own.
10,000 connections, thread each -> 10,000 stacks, constant switching
10,000 connections, event loop -> one stack, one thread, no switchingThis is what Node.js does by default, what Go does with goroutines multiplexed onto a few threads, and what async frameworks in Python and Rust do.
The rough rule
| Work is | Use |
|---|---|
| CPU bound | one process or thread per core, no more |
| I/O bound | async, or many threads |
| Needs isolation | processes |
| Needs shared state | threads, with locks |
More threads than cores does not speed up CPU bound work. It adds switching to the same total amount of computation.