Process vs thread
Understand what each of them actually owns, so you can tell which failures stay contained inside one and which take everything else down with them.
A process owns its own memory; threads inside one share a single copy. That fact decides everything downstream — what a crash takes with it, what has to be locked, and what it costs to start another one.
Two separate workshops, or two benches in one room? Separate workshops mean nobody knocks over anybody else's tools, but shouting between them is slow. In one room passing a tool is instant, and so is spilling paint on somebody else's work — threads share memory much as those benches share a room.
Anyone choosing between running more of something and threading inside one of it has usually already heard that threads are lighter, and that answer settles less than it sounds like it does — it says which one starts faster, not which one fails safer. The property that actually decides the choice is what is shared. A process keeps its memory to itself; a thread started inside one is working in the same memory as every other thread that process has running, and everything about isolation, cost, and locking downstream of the choice follows from that one fact, not from which one gets going quicker.
What is shared decides everything#
A process normally has its own virtual address space, and another process cannot access it merely by using the same address. Operating systems can still authorize cross-process access, and processes can deliberately map shared memory. Threads created inside one process work inside the same address space: the heap, global variables, and open file descriptors are visible to every thread, along with process-wide state such as the current working directory and signal dispositions. Each thread keeps its own stack, registers, program counter and signal mask. Two threads therefore share much of a program’s state while keeping separate execution positions.
Three consequences follow directly from that split, and they tend to get learned as separate topics rather than one fact seen from three angles. Isolation: because a process has its own address space, a bug in one process cannot directly corrupt another process’s memory, so that process’s crash stays inside its own boundary. A thread has no equivalent boundary against its siblings — a buffer overflow in one thread can corrupt data another thread depends on, so when a thread fails badly enough to bring the process down, every thread in it goes with it, because they were never in separate protected memory to begin with. Sharing cost: because threads already share memory, handing data from one thread to another is passing a reference to something already visible on the other side. Moving data between processes has no such shortcut; something has to deliberately copy it across — serialising it onto a pipe or a socket — or both sides have to arrange memory they explicitly agree to share. Synchronisation: shared mutable state is exactly what makes locking necessary in the first place. Threads reading and writing the same structure at once need a lock or an atomic operation standing between them, or the result is a race; two processes with genuinely separate memory have nothing to race over in their own private state, because there is nothing shared unless they went out of their way to set up memory that is.
Change the address-space boundary around a worker
Worker A makes a corrupting write to its own ordinary heap buffer. Predict whether worker B can read those same bytes.
Threads: A and B share the process heap; corrupt bytes can damage B. Separate private address spaces prevent that direct write from changing B's heap.
No deliberate shared mappings, privileged cross-process writes, shared files, IPC or kernel bugs. A caught exception is different from memory corruption. Separate processes can still fail together through shared resources; same-credential processes are not security sandboxes.
What it costs to talk, and to start#
Creating an OS thread often avoids work needed for a new process, but startup costs depend on the creation method and runtime. Fork can share pages until writes, while spawn may initialize a new interpreter; a worker pool amortizes startup. Compare measured startup and ongoing communication/synchronization costs for the workload rather than assuming every thread is faster than every process.
The costs that keep mattering are the ongoing ones. Passing data between processes means deliberately serialising it onto some channel, or setting up memory both sides agreed in advance to share — there is no way around doing that on purpose. Threads skip that step, because passing data between them is passing a reference to memory both sides could already see. What threads pay instead is the price the previous section named: any piece of state more than one thread might touch now needs the care a lock or an atomic operation provides, applied everywhere the sharing actually happens, not once at a boundary. Mutex vs semaphore compares ownership and permit-counting contracts before you choose how threads coordinate. None of this reduces to a fixed figure worth quoting here — the balance shifts with how a given system behaves under its own load, and it is worth measuring on that system rather than assumed from a rule of thumb.
This same boundary — what one unit of running code owns outright versus what it shares with its neighbours — is exactly the kind of groundwork the material under this site’s foundations pillar exists to lay out plainly, on its own terms, before anything gets built on top of it.
Threads used as a fault boundary#
The shape: work gets split into threads so that one slow or misbehaving item cannot hold up the rest — a handler that spawns a thread per request, a worker pool sized to keep the machine busy, a background task moved off the main path so it can fail on its own schedule without taking anything else down. The reasoning sounds right up to the word “thread”: isolating one unit of work from another is exactly what the design wants. It is also exactly what a thread does not provide, because every thread in that pool shares the same address space as every other one in it. An item that corrupts memory it should not touch, or exhausts the process’s heap, does not stay contained to the thread that caused it — it takes every sibling thread down with it, including whatever supervisor was meant to notice the failure and route around it, because that supervisor was running in the same shared memory too.
The tell is in the shape of the failures, not just how often they happen. Instead of one item failing while its neighbours keep running, an unrelated batch of work goes down at the same moment, and a bug that should have stayed local to one kind of task surfaces as unrelated work misbehaving somewhere else entirely. Debugging it starts from the wrong place, because nothing about the crash points back at the thread that actually caused it — every thread in the process shared the same corrupted evidence.
Threads are useful for work that shares a large structure or coordinates tightly. They are a poor fault boundary because a memory error in one thread can damage the whole process. A separate process gives an address-space and crash boundary, but that is not by itself a security sandbox: code launched with the same credentials and access may still read files, use the network or exhaust resources. Running untrusted code requires restricted privileges and explicit limits on those capabilities. Across separate machines there is no shared address space by default, only messages a network can lose, reorder or delay; distributed systems address that separate problem.
| Situation | Take | Because |
|---|---|---|
| One unit's failure must not reach another | Process | Separate address space is the boundary |
| The work shares a large in-memory structure | Thread | Passing a pointer beats copying it |
| Running untrusted code | Sandboxed process | Restrict privileges, resources and access |
| Many short-lived units of the same work | Thread | Creation cost is paid far more often |
- Situation
- One unit's failure must not reach another
- Take
- Process
- Because
- Separate address space is the boundary
- Situation
- The work shares a large in-memory structure
- Take
- Thread
- Because
- Passing a pointer beats copying it
- Situation
- Running untrusted code
- Take
- Sandboxed process
- Because
- Restrict privileges, resources and access
- Situation
- Many short-lived units of the same work
- Take
- Thread
- Because
- Creation cost is paid far more often
IF YOU REMEMBER ONE THING
What is shared decides everything else: the isolation, the cost of passing data, and what has to be locked. Threads are the wrong tool for exactly one requirement — a fault boundary — because a shared address space cannot supply the guarantee that requirement is asking for.
Questions people also ask
5 QUESTIONSAre threads always faster than processes?
Threads often cost less to start because they reuse a process’s address space, but the actual startup cost depends on the OS, runtime, process-creation method and work. Measure the system rather than treating it as an unconditional ordering. That gap barely matters once the work runs for a while — what dominates then is how much copying, serialising, or locking the ongoing work needs, and that can favour either side depending on how much state actually gets shared.
What do threads actually share?
Everything the process itself owns: the address space, so the heap and global variables are the same memory for every thread, plus open file descriptors, the process's current directory, and what each signal is set to do on arrival. In the POSIX model, each thread has its own stack, registers and signal mask, so it can block a signal without blocking it for its siblings. Its stack memory is still inside the shared address space.
Why does a crash in one thread kill the program?
Because the damage is rarely local to begin with. Threads share one address space, so memory corruption or an exhausted heap is not contained to whichever thread caused it — the whole process's memory is now suspect. A fatal process signal or memory corruption can terminate the whole process. An ordinary handled exception can remain local; exhaustion policy and language runtime also affect the outcome.
What is a green thread?
A thread scheduled by a language runtime instead of the operating system, so many can exist without the kernel tracking each one individually. The scheduling detail varies by runtime, but it changes who does the scheduling, not the underlying shape — green threads inside one runtime still generally share one address space the same way.
When should I use multiple processes instead?
Use a separate process when you need a fault boundary or separate resource limits. Untrusted code also needs a security boundary: run it with reduced privileges and explicit filesystem, network and resource restrictions. Merely starting another process with the same access does not sandbox it.