Python GIL
CPython's Global Interpreter Lock serializes bytecode execution to one thread at a time. Threads still help with I/O; for CPU work use processes or the new free-threaded build (PEP 703).
The Global Interpreter Lock is a mutex in CPython that allows only one thread to execute Python bytecode at a time. It exists because CPython's memory management (reference counting) is not thread-safe, and locking every refcount operation individually would be slower than locking once globally.
What this means in practice
- CPU-bound multithreading does not work. Two threads doing pure Python computation run at roughly the same speed as one, sometimes slower due to lock contention.
- I/O-bound multithreading works fine. When a thread blocks in a syscall (read, write, recv), it releases the GIL. Other threads can run. This is why threading is still useful for web scraping, HTTP clients, file processing.
- C extensions can release the GIL. NumPy, Pandas, image libraries do heavy work in C with the GIL released. That is why scientific Python scales.
- Multiprocessing sidesteps the GIL. Each process has its own interpreter and its own GIL. True parallelism, but with IPC overhead.
How it switches
Pre-3.2: every 100 bytecode instructions, the running thread would release and reacquire the GIL.
Post-3.2: switching is time-based. Every 5ms (configurable via sys.setswitchinterval), the current thread releases the GIL and any waiting thread gets a chance. Plus, the GIL is released around every blocking syscall and around sleep.
The new free-threaded build
PEP 703 (accepted) makes the GIL optional. Python 3.13 ships an experimental free-threaded build (python3.13t). It removes the GIL, replaces refcount operations with atomic ops, and adds per-object locks where needed. CPU-bound threading actually scales.
The cost: single-threaded performance is ~10% slower due to atomic overhead. Most C extensions need updates to be safe under free-threading. The transition will be gradual.
When to use what
- I/O-bound, many tasks:
asyncio(single-threaded) orthreading. - CPU-bound, parallel:
multiprocessingorconcurrent.futures.ProcessPoolExecutor. - CPU-bound, vector math: NumPy / SciPy (drops the GIL internally).
- Future:
python3.13twiththreadingfor CPU work.
The GIL is not a bug. It is a design tradeoff that bought CPython simplicity, fast single-threaded performance, and easy C extensions. Free-threading is the bet that the tradeoff has shifted.
Learn more
- DocsPython docs: GlobalInterpreterLockpython.org
- Docs
- Talk