Threads, Async & Event Loops
Threads inside a process, concurrency vs parallelism, threads vs async vs processes, and how C++, JavaScript/TypeScript and Python each map onto the OS.
A thread is an independently scheduled instruction stream inside a process: it has its own stack, registers and instruction pointer, and shares everything else — heap, globals, descriptors — with its siblings, which makes threads cheap to create and communicate through, and easy to corrupt.
Processes buy isolation at the price of expensive creation, expensive switching and explicit communication; threads buy cheap sharing at the price of shared failure — and every runtime, browser and database picks a point on that line for a reason.
There are three ways to have many things in flight — a thread per task, an event loop that parks tasks while they wait, or a process per task — and the choice is decided by whether the work is CPU-bound or I/O-bound, how many tasks you need, and what it costs to have one of them fail.
Concurrency is having several tasks in progress at once, achieved on a single core by interleaving them in time slices; parallelism is executing several at the same instant, which requires several cores — a single core is always concurrent and never parallel.
C++ exposes OS threads directly; JavaScript hides them behind an event loop per realm and reaches the OS through the runtime’s own threads; CPython wraps OS threads but serialises bytecode with the GIL in its default build — three different contracts over the same kernel.
An event loop is a single thread that repeatedly takes the next completed event from a queue and runs its handler to completion; the runtime and the OS do the waiting elsewhere, so one thread can hold thousands of in-flight operations as long as no handler blocks it.
An `await` on an I/O call hands the request to the runtime, which hands it to the OS or a helper thread, frees the calling thread to run other work, and resumes the function as a continuation when the kernel reports completion — with mechanisms that differ between files and sockets and between Node, Python and C++.