Concurrency Comparisons
Side-by-side trade-offs where neither column wins. The workload, the runtime and how much correctness risk you can carry decide — and each comparison ends with the verdict that follows from that, not from a preference.
Concurrency vs parallelismThreads vs processesAsync vs threadsEvent loop vs thread poolMutex vs semaphoreOptimistic vs pessimistic concurrency controlLock-based vs lock-freeBounded vs unbounded queue
Threads vs processes
Both give you parallel execution. They differ in what they share, and everything else — cost, isolation, failure blast radius, how you pass data — follows from that one difference.
| Dimension | Threads | Processes |
|---|---|---|
| Address space | Shared — every object is visible to every thread | Separate — nothing is shared unless you arrange it |
| Data sharing cost | Free (a pointer), but every share needs synchronization | Serialize, copy, deserialize on every message |
| Creation cost | Cheap: a stack and a scheduler entry | Expensive: a full address space and runtime startup |
| Isolation | None — a corrupting write reaches everything | Strong — a crash takes down one worker |
| Crash blast radius | The whole process | One worker; the supervisor restarts it |
| CPython bytecode parallelism | Serialized by the GIL in standard builds | Genuinely parallel |
| Debugging | Races, deadlocks, visibility bugs across the shared heap | Message ordering and lost messages; no shared-memory races |
| Memory footprint | One heap, many stacks | One heap per process — the multiplier people forget |
Use Threads when
- The work genuinely needs to share a large mutable structure.
- Task creation is frequent and each task is short.
- The runtime executes threads in parallel and you are prepared to synchronize.
Use Processes when
- You need isolation: untrusted code, native crashes, leaky libraries.
- You are on CPython and the work is CPU-bound.
- The data passed per task is small relative to the compute per task.
Verdict
Default to processes when the tasks are independent — you trade copies for the elimination of an entire bug class. Reach for threads when the shared structure is genuinely large and hot, and accept that you have taken on the synchronization burden in exchange.