Default CPython has threads, and only one of them executes Python bytecode at a time. A single lock — the GIL — must be held to run Python code. Eight CPU-bound threads on eight cores still don't give us eight-way Python execution.
The shape of the work decides the tool. Waiting on a network? Threads or asyncio are fine, because the GIL is released while waiting. Crunching numbers? Use processes: each one gets its own interpreter and its own GIL, at the cost of memory and some awkwardness in sharing data. Heavy maths? NumPy and similar C extensions release the GIL and get true parallelism.
The lock is also why the ecosystem exists. A single GIL made the interpreter simple and C extensions safe to write, which is arguably why the scientific stack grew in Python at all.
A 2026 note: free-threaded CPython can run without the GIL. Supported, and not yet the default.
The constraint that limits us and the one that made us popular are sometimes the same constraint.
