Skip to the content
Software Made Clear Diagrams that show the mechanism About

Process vs thread

ANSWER

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.

IN PLAIN TERMS

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 owns its own address space, and nothing outside it can read or write into that space directly — the operating system enforces the boundary. Threads created inside a process work inside that same address space: the heap, global variables, and open file descriptors are visible to, and shared by, every thread the process starts, along with process-wide state such as its current working directory and the disposition assigned to each signal — what happens when that signal arrives is the same decision for every thread in the process. What each thread keeps to itself is deliberately narrow by comparison — its own stack, so a function call in one thread does not disturb another thread’s call chain, its own registers and program counter, so each thread has an independent position in the running code, and its own signal mask, so one thread can block a signal without blocking it for its siblings. Two threads in the same process are running the same program against the same data with separate local bookkeeping.

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 wild write from 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.

What it costs to talk, and to start#

Creating a process costs more than creating a thread — setting up a fresh address space and its own process-wide state is more work than adding a stack and a thread’s own bookkeeping to memory that already exists — and that difference is real rather than folklore. It is also rarely the number that should decide anything for work that is going to run for a while, because a one-time startup cost is paid once, and gets swamped by whatever the work goes on to do afterward. Treat “threads are lighter to start” as true and largely beside the point the moment a unit of work outlives its own startup.

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. 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.

None of this makes threads the wrong tool in general — for work that has to share a large structure, or coordinate tightly with its siblings, a thread is exactly right, for the reasons the first section gave. It becomes the wrong tool specifically when the requirement is a fault boundary, when one unit’s failure is not allowed to reach another, because a shared address space cannot provide the one guarantee that requirement is asking for. Everything above also assumes the process and its threads sit on one machine, sharing one kernel’s view of memory. Split the same work across separate machines instead of separate threads, and the sharing this article has spent its length on disappears entirely — there is no address space in common to protect or exploit, only messages that a network can lose, reorder, or simply take a while to deliver. A path on this site is set aside for that version of the problem, the distributed-systems one, with no articles written under it yet.

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 or third-party code Process Isolation is the whole requirement
Many short-lived units of the same work Thread 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 QUESTIONS
Are threads always faster than processes?

Faster at one specific thing: starting. A new thread reuses memory a process already has, so it comes up quicker than a whole new process would. 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. Each thread still keeps its own stack, its own registers, and its own signal mask, so it can block a signal without blocking it for its siblings.

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. The operating system tears down the process as a unit rather than trying to save threads that were never protected from each other.

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?

When the goal is a fault boundary rather than cheap sharing: running code you do not fully trust, keeping one component's crash from reaching the rest, or giving a unit of work its own resource limits. If none of that applies and the work needs to share a large structure constantly, a thread usually fits better.