Multiprocessing runs work in separate processes rather than Python threads. The Global Interpreter Lock (GIL) restricts parallel execution of Python code in threads, but it does not restrict separate processes. Multiprocessing can therefore use multiple CPU cores for pure-Python work. section 1.3.1 section 3.6
A process-based MapReduce worker
For an in-memory MapReduce implementation, `concurrent.futures.ProcessPoolExecutor` provides a process-based alternative to `ThreadPoolExecutor`. The import can change to `from concurrent.futures import ProcessPoolExecutor as Executor`, but that change alone may leave the reduce stage apparently frozen. Inspecting completed futures reveals a pickling error when the worker function is a lambda. Defining it with `def counter(emitted): return emitted[0], sum(emitted[1])` resolves that specific failure. section 3.4 section 3.4.1
Processes communicate through pickle, so code and data sent to workers must be transferable by it or handled separately. File pointers, database connections, sockets, and some foreign-library objects need particular care. Serialization can also be slow enough to erase a parallel speedup. Favor coarse-grained jobs with little communication, and check whether communication—not computation—has become the bottleneck. Shared memory, low-level locks, and complex communication patterns make parallel code harder to debug. section 3.4.1 section 3.6


