PEP 703 in 90 seconds — Python without the GIL is real (and optional)
32 years of GIL — and now an exit door.
The GIL (Global Interpreter Lock) is why pure-Python multi-threading doesn't speed up CPU-bound work: only one thread can execute Python bytecode at a time. For 32 years this was a hard ceiling. Workarounds (multiprocessing, releasing the GIL in C extensions, asyncio for IO-bound) exist; the actual primitive most other languages have (threads that scale CPU-bound) did not.
PEP 703 (Python 3.13, 2024) ships an OPTIONAL no-GIL interpreter build (`python3.13t`, the 't' for 'threaded'). Pure-Python threads now scale linearly on CPU work — at the cost of about 10% single-thread slowdown and a wave of subtle race-condition bugs in code that quietly relied on GIL atomicity. It's officially experimental through 3.13–3.15. By 3.16 (~2027) the default may shift, depending on how the ecosystem holds up. Senior devs need to know it's coming.
# Python ≤ 3.12 (or 3.13 default build)
import threading
counter = 0
def bump():
global counter
for _ in range(100_000):
counter += 1
threads = [threading.Thread(target=bump) for _ in range(8)]
for t in threads: t.start()
for t in threads: t.join()
print(counter)
# Output: 800000 (GIL serialises increments → no race)# Python 3.13t (no-GIL) — same code, same threads:
# counter += 1 is NOT atomic without the GIL.
# Threads interleave: read counter → add 1 → write back.
# Lost updates are now real.
print(counter)
# Output: 487213 (or any number under 800000, varies per run)
# Fix: use threading.Lock() or atomic counter from C extension.🎯 Predict the output
On the no-GIL build, what's the maximum SAFE value `counter` could end at across 8 threads × 100,000 increments? (Hint: races lose updates, never gain them.)
# 8 threads × 100,000 iterations each.
# Each iteration: counter += 1 (read → add → write, non-atomic).
# What's the MAX value of counter after all threads finish?