What is the GIL?
The Global Interpreter Lock (GIL) is a mutex in CPython, the standard Python interpreter, that allows only one thread to execute Python bytecode at a time. A program can create as many threads as it wants, but only one of them holds the GIL and runs Python code at any given moment.
This single lock is the reason CPU-bound Python threads don’t speed up on multi-core machines, and it’s the thing that free-threaded Python removes.
Why CPython has the GIL
CPython manages memory through reference counting: every Python object carries an integer that tracks how many names or containers point to it, and the object is freed the moment that count hits zero. Without a lock, two threads incrementing or decrementing the same counter at the same time can corrupt it, leaking objects or freeing them while still in use.
When Guido van Rossum wrote CPython in the early 1990s, commodity hardware had one core. A single lock protecting the interpreter was the cheapest way to make reference counting thread-safe, and the cost (no thread parallelism) was invisible on a machine that couldn’t run threads in parallel anyway. The same lock also gave C extension authors a guarantee: any code called while the GIL is held can safely touch Python objects without writing its own locking. That guarantee accelerated the growth of CPython’s C extension ecosystem, from NumPy to database drivers, because extension authors didn’t have to reason about concurrent access to Python internals.
By the time multi-core machines became standard in the mid-2000s, the C extension ecosystem depended on the GIL’s guarantees, and removing the lock would have broken extensions faster than it would have helped application code.
Why the GIL stalls CPU-bound threads
A CPU-bound workload that splits work across four threads and runs on four cores barely improves wall-clock time over using one thread. The GIL forces the threads to take turns executing bytecode, so only one core does useful Python work at any moment. The others sit idle or run the GIL’s release-and-acquire handshake.
This affects pure-Python computation: tight loops and number crunching in Python-level arithmetic.
When the GIL steps aside
The GIL is released in several situations, so not all concurrent Python programs suffer from it:
- I/O operations. CPython releases the GIL during blocking I/O (network calls, file reads,
time.sleep). An I/O-bound server handling many connections with threads benefits from concurrency even with the GIL, because threads that are waiting on I/O aren’t holding the lock. - C extensions that explicitly release the GIL. Libraries like NumPy, SciPy, and Pillow drop the GIL around their C/Fortran/C++ compute kernels. A NumPy matrix multiplication runs at full speed on all cores regardless of the GIL, because the work happens in C code that has released the lock.
multiprocessing. Each process has its own interpreter and its own GIL. CPU-bound work can scale across cores by spawning processes instead of threads, at the cost of inter-process communication overhead and higher memory use.asyncio. Async I/O runs in a single thread with cooperative scheduling. The GIL is irrelevant because there is only one thread executing Python code.
Why removing it took 30 years
Removing the GIL required solving two problems at once: keeping reference counting thread-safe without a global lock, and doing so without slowing down single-threaded code (which is most Python programs).
In 2016 Larry Hastings presented the “Gilectomy,” a CPython fork that removed the GIL and used fine-grained locking. It proved that free-threaded CPython was possible, but single-threaded overhead was too high for the approach to be adopted.
In 2023 Sam Gross proposed PEP 703: Making the Global Interpreter Lock Optional in CPython with a different approach: biased reference counting. Each object stores an owning-thread id plus separate local and shared reference counts. The owning thread updates the local count with non-atomic operations; other threads update the shared count atomically. Because most objects stay on the thread that created them, the fast path dominates. The Steering Council accepted PEP 703 in October 2023 with a phased rollout.
How free-threaded Python ships today
Python 3.13 shipped the free-threaded build as experimental. Python 3.14 promoted it to officially supported under PEP 779: Criteria for supported status for free-threaded Python, based on the single-threaded performance gap closing to 5-10%.
The default CPython build still has the GIL on every supported version. The free-threaded build is an opt-in variant, installed separately (for example, python3.14t via uv). Whether and when the free-threaded build becomes the default is a separate, future decision that PEP 779 explicitly leaves open.
Learn More
- The official Python glossary entry for the GIL
- Python support for free threading (HOWTO)
- What is PEP 703? explains the proposal that removes the GIL
- What is PEP 779? covers the criteria for supported status
- Try free-threaded Python with uv walks through measuring the GIL’s effect on thread parallelism
- How to use free-threaded Python in a uv project adopts the free-threaded build in a real project