Multiprocessing
In computing, multiprocessing is the use of two or more central processing units within a single computer system, and the techniques that coordinate their work. Unlike multithreading, where multiple threads share a single address space and compete for the same memory, multiprocessing gives each process its own isolated memory, its own file descriptors, and its own runtime state. This isolation is not a bug but a design decision: processes cannot corrupt each other's state, and the failure of one process does not bring down the system. Multiprocessing is the architecture of resilience, purchased at the cost of communication overhead.
The fundamental challenge of multiprocessing is coordination. Processes must share data, synchronize state, and avoid race conditions without the convenience of shared memory. The mechanisms for this coordination — message passing, shared memory segments, pipes, and sockets — each encode a different philosophy about how independent agents should collaborate. Message passing, championed by the actor model, treats processes as autonomous entities that communicate only through immutable messages. Shared memory segments violate the isolation principle for performance, reintroducing the very synchronization problems that multiprocessing was designed to avoid.
The Python Paradox
The tension between multiprocessing and multithreading is nowhere more visible than in Python. The Global Interpreter Lock (GIL) prevents multiple threads from executing Python bytecode simultaneously, making multithreading ineffective for CPU-bound tasks. Python's response was the \`multiprocessing\` module, which spawns entire Python interpreters as child processes, each with its own GIL, and coordinates them through pickling and pipes. The result is a language whose primary concurrency mechanism is not threads but full process isolation — a bizarre outcome for a language that prides itself on simplicity.
The Python case reveals a deeper truth: multiprocessing is often not chosen for its architectural virtues but adopted as a workaround for the limitations of threads. The GIL made multiprocessing necessary; necessity made it mainstream. A generation of Python programmers learned concurrency through process pools and inter-process queues, not through the shared-memory primitives that dominate systems programming. This is not wrong, but it is contingent: the dominance of multiprocessing in Python is an accident of implementation, not a discovery of principle.
Multiprocessing and the Hardware Revolution
Multiprocessing was once the exclusive domain of servers and supercomputers. Now it is ubiquitous: every smartphone contains multiple CPU cores, and every laptop expects parallel software. The hardware transition was abrupt and largely complete by 2010; the software transition is still ongoing. Most code is still single-threaded, and parallelizing it requires rethinking assumptions about execution order, memory consistency, and error handling that were encoded decades ago.
The emergence of SIMD instructions and GPU computing has further complicated the landscape. SIMD parallelizes within a single core; GPUs parallelize across hundreds of lightweight threads; multiprocessing parallelizes across independent processes. These are not competing paradigms but complementary layers of the parallelism hierarchy, and effective systems must compose them. A modern data pipeline might use multiprocessing to distribute work across CPU cores, SIMD to vectorize operations within each core, and a GPU for the most parallelizable subtasks. The challenge is no longer how to parallelize, but how to choose the right granularity.
Multiprocessing is the coarsest grain of parallelism, and in a world of increasingly fine-grained parallel hardware, its role is evolving. It is no longer the primary mechanism for performance; it is the mechanism of last resort, the boundary that separates failures, the architecture that ensures that when one component crashes, the others survive. In this sense, multiprocessing is becoming less about speed and more about safety — a shift from performance engineering to systems engineering that mirrors the broader evolution of computing toward reliability at scale.
The assumption that multiprocessing and multithreading are alternatives — that a system must choose one — is a false dichotomy born of operating system textbooks. Real systems use both: threads for shared-state parallelism within a trust boundary, processes for isolation across boundaries. The question is not which to use, but where to draw the line between them. And that line is a security boundary, not a performance choice.