Processes, Threads & Tasks
Processes, threads, tasks and coroutines as distinct things a runtime can hand you — and the language-specific reality of what each one buys in C++, JavaScript and CPython. The kernel-side mechanics live in Operating Systems.
Separate address spaces, explicit communication, contained crashes. The kernel mechanics live in Operating Systems; what matters here is the design consequence — a process model makes shared mutable state impossible by construction and makes every piece of sharing you actually need visible in the code.
Q · What does running work in separate processes actually buy me, and what does it stop being able to protect?
What a thread is belongs to Operating Systems. What matters here is the consequence of the design: every thread in a process can reach every object every other thread can reach, with no ceremony, no declaration and no error. Sharing is the default, and defaults are what you forget to check.
Q · If every thread can reach every object, which of those objects actually needs protecting, and how do I know?
Six dimensions decide it: isolation, memory sharing, startup cost, communication cost, crash impact and access to cores. Operating Systems compares the mechanisms; this lesson is the choice, and the choice is usually made by the crash column rather than the performance one.
Q · For this unit of work, do I want an execution stream that shares everything or one that shares nothing?
The abstraction most engineers skip past. A task is a unit of work the runtime may schedule; a thread is an execution stream the OS schedules. Ten thousand tasks can live on four threads. Everything confusing about async — why it scales, why it stalls, why single-threaded code still races — follows from this one gap.
Q · When I spawn ten thousand tasks, what actually exists — and what is running?
An ordinary function has two control points — call and return. A coroutine has four: call, suspend, resume, return. That single addition is what lets one thread hold ten thousand in-progress operations, and it is also what silently removes the atomicity your code was relying on without ever saying so.
Q · What does it mean for a function to pause in the middle, and who decides when it resumes?
In standard CPython builds, one lock serialises bytecode execution, so CPU-bound threads do not scale across cores — while I/O-bound threads and asyncio work fine, because the lock is released around blocking calls. Processes have separate interpreters and do scale. Free-threaded builds exist and change this, and which one you have is a property of your build.
Q · Why does adding threads to a CPU-bound Python program not make it faster, and what does?
JavaScript *execution* is one event loop per agent — but the runtime around it is thoroughly multi-threaded: Node does file and crypto work on a thread pool, network I/O through the kernel's readiness API, and both Node and browsers offer real parallelism through workers with message passing.
Q · If JavaScript runs one piece of code at a time, what exactly is doing the other work — and where does real parallelism come from?
std::thread, std::async, std::mutex, std::atomic — and, since C++11, a formally defined memory model in which a data race is undefined behaviour. Not "you get a stale value": undefined, meaning the compiler was entitled to assume it could not happen and optimised on that basis.
Q · What does C++ actually promise about concurrent memory access, and what happens when I break the promise?