Execution Modelcritical

GIL (Global Interpreter Lock)

A mutex in CPython that allows only one thread to execute Python bytecode at a time.

Memory anchor

GIL = Giant Invisible Leash. Imagine all your threads are dogs on one leash — only one can run ahead at a time. But when a dog stops to sniff (I/O), the leash lets the next dog go.

Expected depth

The GIL exists to protect CPython's reference-counting memory management from race conditions. It serializes thread execution — multi-threading gives NO CPU parallelism for CPU-bound tasks. However, the GIL is released during I/O operations, so threading is still useful for I/O-bound work.

Deep — senior internals

The GIL is a per-interpreter lock. Python 3.12 introduced per-subinterpreter GILs (PEP 684). Python 3.13 added an experimental free-threaded build (PEP 703) that removes the GIL entirely. In Python 3, the GIL is released based on a time interval (sys.getswitchinterval(), default 5ms) — not every N bytecode instructions as in Python 2. C extensions can explicitly release the GIL (Py_BEGIN_ALLOW_THREADS) — NumPy and OpenSSL do this, enabling true parallelism in their operations. The GIL's overhead actually slows down multi-threaded CPU-bound code compared to single-threaded.

🎤Interview-ready answer

The GIL ensures only one thread runs Python bytecode at a time in CPython. It protects reference counting from race conditions. For CPU-bound tasks, this means threading gives no speedup — use multiprocessing. For I/O-bound tasks, threading works well because the GIL is released during I/O waits. Python 3.13 introduced an experimental free-threaded build (PEP 703) that removes the GIL for true thread parallelism.

Common trap

The GIL does NOT prevent all concurrency. I/O-bound programs benefit greatly from threading because the GIL releases during I/O. Also, C extensions like NumPy explicitly release the GIL — so NumPy operations run in parallel across threads.

Related concepts