Processes vs Threads: A Beginner's Guide With Real Examples
Learn the real difference between processes and threads, how the operating system schedules them, when to pick one over the other, and why your browser uses both at the same time.
Processes and threads are the two ways an operating system runs work in parallel — and confusing them is one of the most common mistakes in concurrent programming. A process is a fully isolated running program with its own memory; a thread is a lightweight unit of execution that shares memory with other threads inside the same process. Pick the wrong one and you get either a memory disaster, a debugging nightmare, or a 10x performance regression.
This guide explains both in plain English, walks through where each shines (and where each ruins your week), and gives you a clear decision tree.
The One-Line Difference
A process has its own private memory; a thread shares memory with other threads in the same process. Everything else — speed, safety, debugging difficulty, scaling — flows from that single fact.
If two units of work need to share data quickly, threads. If they need to be isolated and crash-safe, processes.
How Processes Work
When you launch an app, the OS creates a process: a fresh virtual address space, its own file descriptors, its own environment variables. Two processes cannot see each other's memory unless they explicitly set up shared memory or pipes. If one crashes, the OS reaps it and the others keep running.
Processes are expensive to create — the OS has to set up page tables, copy environment, allocate kernel structures. Modern Linux can create maybe 10,000 processes per second per core. They are also expensive to switch between, because the CPU has to flush caches and reload the page table.
But they are safe. A bug in one process cannot corrupt another. This is why Chrome runs each tab as its own process: a malicious page cannot read your banking tab's memory.
How Threads Work
Inside a single process, you can create multiple threads. Each thread has its own stack and CPU registers, but they all share the same heap, the same global variables, the same file descriptors. Switching between threads is cheap — no page-table reload, no cache flush.
Threads make sharing data trivial. They also make sharing data dangerous: two threads writing to the same variable without coordination is a race condition, and race conditions are the most painful bugs in software. You will need locks, atomics, channels, or the language's own concurrency primitives to stay sane.
Side by Side at a Glance
| Dimension | Process | Thread |
|---|---|---|
| Memory | Private | Shared with siblings |
| Crash blast radius | Just this process | Whole process |
| Creation cost | Expensive (ms range) | Cheap (μs range) |
| Switching cost | Expensive | Cheap |
| Data sharing | Pipes, sockets, shared mem | Direct memory access |
| Concurrency hazards | Few (isolation) | Many (races, deadlocks) |
| Best for | Isolation, security, fault tolerance | Tight-coupled parallelism, low latency |
A Worked Example: Chrome Uses Both
Chrome is the textbook case. Each browser tab is a separate process (renderer process). This is why a runaway JavaScript loop in one tab does not freeze your other tabs and why Spectre-style attacks are mitigated.
Inside each renderer process, dozens of threads do specific jobs: the main thread runs JS, the compositor thread paints frames at 60 Hz, worker threads handle image decoding. They share the page's DOM data structures directly because they live in the same memory space.
This hybrid is the modern playbook: processes for isolation, threads for parallelism within a unit of isolation.
When to Pick Threads
Pick threads when:
- The work units share lots of data and would be slow to copy across processes.
- Latency matters and you cannot afford process creation/IPC cost.
- You need many concurrent units (web servers handling thousands of connections).
- You are I/O-bound and want a single process to keep the CPU busy.
Examples: a web server like Tomcat, a game engine's audio + physics + rendering, a database query engine running parallel scans.
When to Pick Processes
Pick processes when:
- Crashes must be contained (browsers, plugin systems, untrusted code).
- You need real CPU parallelism in a language with a Global Interpreter Lock (Python's CPython, Ruby).
- The work units are loosely coupled and rarely need to talk.
- Security boundaries matter (sandboxes, multi-tenant systems).
Examples: Chrome's per-tab isolation, Postgres's per-connection backend processes, Python multiprocessing for CPU-bound work, Nginx worker processes.
The Python Special Case
Python's CPython interpreter has a GIL (Global Interpreter Lock) that allows only one thread to run Python bytecode at a time per process. So:
- For I/O-bound Python work (web requests, file I/O), threads are great — they release the GIL while waiting.
- For CPU-bound Python work (number crunching, image processing), threads do not help — use
multiprocessingor move the hot path to NumPy/Cython.
In 2026 the no-GIL build (PEP 703) is finally stable opt-in, but most production deployments are still on the GIL build. Plan accordingly.
# CPU-bound: threads useless, processes work
from multiprocessing import Pool
with Pool(8) as p:
results = p.map(crunch_numbers, big_dataset)Common Mistakes Beginners Make
- Spinning up one thread per task forever. Use a thread pool. Unbounded threads exhaust memory fast.
- Sharing mutable data between threads without locks. A race condition will appear in production a week later.
- Using
multiprocessingfor trivial work. The startup cost (~50ms per process) destroys speedup on small jobs. - Mixing threads and
fork(). Forking a multi-threaded process in Linux is officially a footgun. Spawn instead. - Assuming "more threads = faster". Past your physical core count, you mostly add context-switch overhead.
Quick Reference
| You need... | Default answer |
|---|---|
| Isolated, crash-safe units | Processes |
| Cheap parallelism with shared state | Threads |
| Real CPU parallelism in CPython | Processes |
| Handle 10k network connections | Threads (or async) |
| Sandbox untrusted code | Processes |
| Low-latency in-game systems | Threads |
Rune AI
Key Insights
- Process = private memory, crash-safe, expensive to create.
- Thread = shared memory, cheap, dangerous without locks.
- Use processes for isolation; use threads for tight-coupled parallelism.
- Python's GIL means threads do not help CPU-bound work — use
multiprocessing. - The modern Chrome-style template is both: process per isolation boundary, threads inside each.
Frequently Asked Questions
What is a "green thread" or "fiber"?
Are async/await and threads the same?
How many threads should my web server use?
Why do databases prefer processes?
Are threads dying because of async?
Conclusion
Processes and threads are the two halves of OS-level concurrency, and the one-line rule covers 90% of decisions: use processes for isolation and crash safety, use threads for cheap parallelism with shared memory. Real systems combine them — Chrome's tabs-as-processes plus threads-per-tab is the modern template. Once you internalise the trade-off, concurrent code stops being scary and becomes another design choice. The scariest bugs in your career will be race conditions; the boring fix is to copy what Chrome does.