Concurrency line
The Python GIL
Python lets only one cook use the stove at a time, so hiring more cooks does not always make dinner faster.
Stops explain each step. The film plays the whole ride.
- Cooking
- Waiting or asking
- Handed over
- Something went wrong
Cook 1 puts on the hat
Python’s kitchen has one stove with several burners, but only one chef’s hat. Only the cook wearing the hat may cook. Cook 1 puts it on.
Next stop: Cook 1 cooks on the stove
All stops
The everyday version
A kitchen has one stove and one chef’s hat. Only the cook wearing the hat may cook. Everyone else waits. A cook who steps away to wait at the delivery door hangs the hat back up.
- The first cookis the Thread 1
Puts on the hat, cooks, and hangs it back up when asked.
- The second cookis the Thread 2
Waits for the hat. After a few moments, politely asks for it.
- The chef’s hatis the The GIL
Only one exists. Whoever wears it is the only one allowed to cook.
- The stoveis the Interpreter
Where the cooking happens, with one cook at a time.
- The delivery dooris the Network / disk
Waiting for a parcel needs no stove, so the hat goes back on the hook.
- The rice cookeris the C extension
Cooks on its own while the cook does something else. A good one needs no hat at all.
- Two long dishes, one hatis two cooks with endless work
Both cooks need the stove all the time. More cooks will not speed it up, because only one may wear the hat.
Interview questions
Say the short answer first. Go deeper only if they ask.
What is the GIL and what does it protect?
A mutex in CPython so only one thread executes Python bytecode at a time.
It keeps the object model, including built-ins like dict, safe against concurrent access, and simplifies reference counting. It trades multi-core parallelism for simplicity.
“It makes my code thread-safe.”
When is the GIL released?
During blocking I/O, in C extensions that release it, and on forced switches.
The glossary says it is always released when doing I/O. Hashing, compression and numeric extensions often release it. A waiting thread also forces a switch after getswitchinterval(), default 5 ms.
“Never. Threads in Python are fake.”
Why do CPU-bound threads not speed up?
Only one thread executes bytecode at a time.
Threads alternate on the GIL, so total CPU time is unchanged and switching adds overhead. Use processes, or native code that releases the GIL.
“Python threads are green threads on one core.”
What does sys.setswitchinterval do?
It sets the ideal time slice, 5 ms by default, before a waiting thread requests the GIL.
It is advisory: the switch happens at the next eval-breaker check, and which thread runs next is the OS’s decision. Lower values add responsiveness and overhead.
“It is how many bytecodes run before a switch.”
Does the GIL make counter += 1 atomic?
No.
It compiles to several bytecodes: load, add, store. A switch between them loses an update. The docs FAQ lists i = i+1 as not atomic. Use a Lock.
“Yes, the GIL makes everything atomic.”
Is the GIL removed in Python 3.14?
No. Free-threaded builds are officially supported but optional. The GIL build is still the default.
3.13 introduced the experimental t build. PEP 779 made it supported in 3.14, with roughly 5-10% single-thread overhead. Extensions must support it, or the GIL is re-enabled.
“3.13 and 3.14 removed the GIL for everyone.”
What are the alternatives for CPU parallelism?
Processes, native extensions, subinterpreters and free-threaded builds.
multiprocessing and ProcessPoolExecutor give each process its own GIL. NumPy, Cython or Rust can release it. 3.14 adds InterpreterPoolExecutor, built on the per-interpreter GIL of PEP 684.
“Rewrite it in Go” as the only answer, or “use asyncio.”
Does asyncio bypass the GIL?
No. It is single-threaded and concurrent, not parallel.
Its win is cheap waiting on many sockets, not running Python on several cores. A CPU-heavy coroutine blocks the whole event loop.
“asyncio gets around the GIL.”
Cheat-sheet
Commands you will actually type.
import sys; sys.getswitchinterval() # 0.005Current switch interval in seconds.
sys.setswitchinterval(1e-6)Teaching only: makes races easier to reproduce. Not a fix.
import sys; sys._is_gil_enabled() # 3.13+Is the GIL on right now?
python3.14t -X gil=0 script.py # or PYTHON_GIL=0Run a free-threaded build with the GIL off. `=1` forces it on.
import sysconfig; sysconfig.get_config_var("Py_GIL_DISABLED") # 1 on a t buildDoes this build support free threading?
import dis; dis.dis("counter += 1")Shows several bytecodes that a switch can split.
import hashlib, threading # hashing big buffers can overlap on threadsAn example of C work that releases the GIL.