Free-Threaded Python: What Removing the GIL Means for You
For decades, CPython's Global Interpreter Lock (GIL) allowed only one thread to run Python bytecode at a time. Threads helped with I/O, but not with CPU-heavy work. Free-threaded builds of Python remove the GIL.
The timeline
- Python 3.13 introduced an experimental free-threaded build.
- Python 3.14 made the free-threaded build officially supported. It's still a separate build; the default Python keeps the GIL.
Try it
uv python install 3.14t # the 't' suffix means free-threaded
uv run --python 3.14t python -c "import sys; print(sys._is_gil_enabled())"
# False
See the difference
import time
from concurrent.futures import ThreadPoolExecutor
def cpu_work(n: int) -> int:
total = 0
for i in range(n):
total += i * i
return total
start = time.perf_counter()
with ThreadPoolExecutor(max_workers=4) as pool:
list(pool.map(cpu_work, [10_000_000] * 4))
print(f"{time.perf_counter() - start:.2f}s")
# With the GIL: about the same as running them one after another.
# Free-threaded: close to 4x faster on a 4+ core machine.
What it means in practice
- CPU-bound Python code can use threads for real parallelism instead of multiprocessing, with shared memory and no pickling.
- Single-threaded code may run slightly slower on the free-threaded build, due to the extra safety mechanisms.
- C extensions must declare support for free-threading. If an imported extension doesn't, Python re-enables the GIL and prints a warning. Check your key dependencies.
- Thread safety is now your job: code that relied on the GIL to make operations effectively atomic needs proper locks.
Should you switch?
For web apps that mostly wait on databases and APIs, async or ordinary threads are already fine. Try the free-threaded build if you have CPU-heavy Python code (data processing, simulations, parsing) and want to parallelise it with threads, and test your dependencies first.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.