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
Event loop vs thread pool
Two server architectures for the same problem: many clients, limited machine. They fail differently under load, and the failure mode is usually what should pick between them.
| Dimension | Event loop | Thread pool |
|---|---|---|
| Concurrency ceiling | Bounded by memory per connection — very high | Bounded by thread count and stack memory |
| Cost of an idle connection | A socket and a small object | A whole thread, if it is thread-per-connection |
| One slow handler | Head-of-line blocking for every other connection | Occupies one worker; the rest keep serving |
| CPU-bound request | Catastrophic without offloading | Absorbed, until all workers are busy |
| Uses multiple cores | Needs one loop per core, or worker threads | Naturally, if the runtime runs threads in parallel |
| Shared-state bugs | Only across await points | Anywhere two handlers touch the same object |
| Saturation signal | Event-loop lag rises before throughput drops | All workers busy, queue depth and age climbing |
| Mental model | One callback at a time, never interrupted mid-callback | N handlers running truly simultaneously |
Use Event loop when
- Many long-lived, mostly idle connections — chat, streaming, SSE.
- Handlers are thin: parse, call something, serialize.
- You can prove nothing CPU-heavy runs on the loop.
Use Thread pool when
- Handlers do meaningful computation or call blocking libraries.
- Concurrency is in the hundreds and per-request memory is acceptable.
- You want one slow request to hurt one worker, not everyone.
Verdict
The question is not which is faster; it is which failure you prefer. An event loop degrades globally and gracefully until one handler stalls it; a pool degrades locally and then queues. Most production stacks pick the loop for the edge and a bounded pool for anything that computes.