The lock that keeps Python’s interpreter orderly

In the standard implementation of Python, CPython, the Global Interpreter Lock is a mutex: a lock that lets only one thread at a time access key parts of the interpreter. Python objects can be created, changed, and discarded constantly; the lock helps keep those operations from colliding inside the interpreter’s shared bookkeeping.

A thread that wants to run Python instructions must hold the lock. If another thread has it, the first waits. CPython periodically gives threads a chance to take turns, but taking turns is not the same as doing the work simultaneously. On a multi-core processor, several threads may be ready while only one is executing Python code.

Why threads still help with waiting

The lock matters most when a program is limited by computation, such as repeatedly processing numbers. In that case, one thread’s work can keep the lock occupied, leaving other Python threads unable to use extra cores for the same task.

But many programs spend time waiting for something outside the processor: a network reply, a file read, or a database response. While a thread is blocked on that kind of I/O, CPython can let another thread run. Threads can therefore help a program handle several waiting tasks without making CPU-heavy Python calculations run in parallel.

For CPU-bound work, programs often use separate processes instead. Each process has its own interpreter and lock, so multiple processes can run Python code on different cores, at the cost of extra memory and the need to coordinate or exchange data. Some native libraries can also release the GIL while doing work outside the Python interpreter.

The practical distinction is simple: threads can overlap waiting, while ordinary Python code in CPython cannot use them to multiply CPU work across cores.