Python 3.14.6 landed on June 10. Buried in the changelog is the announcement developers have been arguing about for a decade: PEP 779 is accepted, and Python’s Global Interpreter Lock is now officially optional — not experimental, not a build flag with asterisks, but officially supported. This is Phase II of the GIL removal roadmap, and it matters.
The headline writes itself: “GIL is dead.” Ignore that headline. The accurate version is harder to tweet but more useful: the GIL-free Python build is now a first-class citizen, and whether you should care depends entirely on what your code actually does.
What PEP 779 Actually Changed
Python’s GIL removal follows a three-phase roadmap under PEP 703. Phase I (Python 3.13) made a free-threaded build available but explicitly experimental. Phase II is where we are now: officially supported, optional. Phase III — making the GIL-free build the default — has no confirmed timeline yet.
“Officially supported” is doing a lot of work in that sentence. It means the design is finalised, the APIs are stable, and the CPython team considers the performance trade-off acceptable. It does not mean running pyenv install 3.14 gets you a GIL-free interpreter. You still have to ask for it: pyenv install 3.14t. The t suffix is the tell.
The Numbers That Changed the Equation
In Python 3.13’s free-threaded build, single-threaded code ran roughly 40% slower than the standard interpreter. That killed adoption before it started — no team absorbs a 40% single-threaded regression to gain parallel threads. Python 3.14t fixes this: the overhead drops to 5-10%.
That shift changes the calculus. CPU-bound multi-threaded workloads see 2-4x speedups on a 4-core machine, with some benchmarks hitting 8x in compute-heavy scenarios. You pay 5-10% on single-threaded code. For most teams with CPU-bound workloads, that’s a reasonable trade.
I/O-bound code is a non-story. The GIL was already released during network and disk waits, so free-threaded Python changes nothing for async or I/O-heavy services. If your bottleneck is database queries or HTTP requests, you’re not buying anything here.
| Scenario | Standard Python 3.14 | Free-Threaded 3.14t |
|---|---|---|
| CPU-bound (4 threads, 4 cores) | ~2.0s | ~0.55s (3.6x faster) |
| Single-threaded | Baseline | 5-10% slower |
| I/O-bound | Baseline | No meaningful change |
What True Parallelism Looks Like
Here’s a minimal example. This CPU-bound loop hits the GIL hard under standard Python, but runs genuinely parallel under the free-threaded build — same threading module, no changes to the threading model:
import threading, time
def crunch(n):
total = 0
for _ in range(n):
total += 1
return total
threads = [threading.Thread(target=crunch, args=(10_000_000,)) for _ in range(4)]
start = time.perf_counter()
for t in threads: t.start()
for t in threads: t.join()
print(f"{time.perf_counter() - start:.2f}s")
# Standard Python 3.14: ~2.0s
# Free-threaded 3.14t: ~0.55s (4-core machine)
The Ecosystem Reality: Check Before You Switch
As of June 2026, roughly 183 of 360 tracked packages on PyPI publish free-threaded wheels — barely half the ecosystem. The community compatibility tracker is the first place to check before migrating anything.
The headline packages: NumPy 2.0+ ships free-threaded wheels and is thread-safe for most operations. SciPy has pre-compiled wheels. FastAPI 0.136.0 officially supports the free-threaded build. scikit-learn is thread-safe for inference but not training. PyTorch support is in progress.
CPython handles incompatible packages gracefully: if a C extension hasn’t been marked as free-thread-safe, the interpreter quietly re-enables the GIL for that process. Your code won’t crash — it’ll just revert to GIL-mode behavior. Verify what you’re actually running: python -c "import sys; print(sys._is_gil_enabled())"
The Risk Nobody Mentions: Race Conditions
Here’s what most coverage skips. Some Python code has been implicitly relying on the GIL for thread safety — two threads mutating a shared list without explicit locking, for example. Under the GIL, those threads interleaved safely. Under free-threading, they run truly in parallel, and that shared mutable state becomes a data race.
No exception is thrown. The code produces wrong results under concurrent load. If you’re moving CPU-bound code to free-threaded Python and it touches shared state across threads, add threading.Lock() around it. This is correctness, not optimization.
How to Try It Today
pyenv install 3.14t # install the free-threaded build
pyenv local 3.14t # use it in current directory
python -Xgil=0 script.py # disable GIL explicitly at runtime
python -c "import sys; print(sys._is_gil_enabled())" # verify: should print False
Before migrating anything real, audit your dependencies at py-free-threading.github.io/tracking/. If your critical packages are in the green column, start testing on staging. If PyTorch or a heavy C extension is on your list, wait for the next cycle.
The Bottom Line
PEP 779 is the most significant change to Python’s concurrency model in the language’s history. The 5-10% single-threaded overhead — down from 40% in Python 3.13t — is the milestone that makes adoption genuinely feasible. Teams with CPU-bound workloads and clean dependency trees should be testing this now.
But “viable for testing” and “ready for production” are different sentences. Audit your dependencies. Run the benchmarks on your actual workload. Phase III — when GIL-free becomes the default — is still ahead. This is the right moment to get ahead of it, not to bet the stack on it. The Python 3.14.6 release page has everything you need to get started.













