Ismat Samadov
  • Tags
  • About
17 min read/1 views

The GIL Is Finally Dead: Free-Threaded Python Is Production-Ready in 2026

Python 3.14's free-threaded build is officially supported. 10x speedups on CPU-bound tasks, 51% package compatibility, and Django runs without the GIL.

PythonPerformanceProgrammingOpen Source

Related Articles

Semantic Caching Saved Us $14K/Month in LLM API Costs

14 min read

LLM Evals Are Broken — How to Actually Test Your AI App Before Users Do

14 min read

Your Database Indexes Are Wrong — The $40K/Month Query You Don't Know About

13 min read

Enjoyed this article?

Get new posts delivered to your inbox. No spam, unsubscribe anytime.

On this page

  • The 40-Year-Old Lock
  • The Sam Gross Breakthrough
  • What Changed Between 3.13 and 3.14
  • The Benchmarks That Matter
  • Ecosystem Readiness: The 51% Mark
  • How to Actually Use It
  • Installation
  • Your First Free-Threaded Program
  • Installing Packages
  • When Free-Threaded Python Makes Sense (And When It Doesn't)
  • The C Extension Challenge
  • Django, Flask, and Web Frameworks
  • What I Actually Think
  • Sources

© 2026 Ismat Samadov

RSS

For thirty-three years, Python developers lived with a single constraint that shaped everything: the Global Interpreter Lock. The GIL meant that no matter how many CPU cores your server had — 4, 16, 128 — Python would use exactly one of them for Python bytecode execution. Multi-threaded Python programs weren't truly parallel. They were taking turns.

In October 2025, Python 3.14 shipped with a free-threaded build that is no longer experimental. The Python Steering Council accepted PEP 779, officially moving free-threaded Python from "experimental" to "supported." The GIL is still there in the default build. But for the first time, you can run production Python with true thread-level parallelism — and the ecosystem is actually ready for it.


The 40-Year-Old Lock

The GIL was introduced in CPython's earliest days as a simple solution to a hard problem: thread safety. CPython uses reference counting for memory management. When two threads modify the same object's reference count simultaneously without protection, you get memory corruption, segfaults, and undefined behavior.

The GIL solved this by making Python threads mutually exclusive. Only one thread can execute Python bytecode at a time. Other threads wait. This made CPython's memory management trivially thread-safe — at the cost of making CPU-bound multi-threading useless.

For I/O-bound workloads (web servers, network clients, file processing), the GIL was fine. Python releases the GIL during I/O operations, so threads can overlap their waiting. But for CPU-bound workloads (data processing, numerical computation, image manipulation), Python threads provided zero parallelism. You had to use multiprocessing instead — which spawns separate OS processes with separate memory spaces, separate interpreters, and all the overhead that comes with inter-process communication.

Attempts to remove the GIL go back decades. Larry Hastings' "GIL-ectomy" work in 2016 showed it was possible but incurred a 30-40% single-threaded performance penalty. The Python community's response: too expensive.

Then Sam Gross changed the math.


The Sam Gross Breakthrough

In 2021, Sam Gross — a software engineer at Meta — published a fork of CPython with the GIL removed that achieved something previous attempts hadn't: minimal single-threaded overhead. His approach used biased reference counting, deferred reference counting, and careful lock-free data structures to maintain thread safety without a global lock.

Gross's work led to PEP 703 — "Making the Global Interpreter Lock Optional in CPython" — which he submitted in January 2023. The Steering Council accepted it with a three-phase rollout plan:

PhasePython VersionStatusDescription
Phase I3.13 (Oct 2024)CompleteExperimental free-threaded build behind --disable-gil
Phase II3.14 (Oct 2025)CurrentOfficially supported, still optional
Phase IIIFuturePlannedFree-threaded build becomes the default

Meta contributed engineering resources to make this happen, funding work on both the CPython implementation and ecosystem compatibility. Quansight Labs led the community coordination effort, tracking package compatibility and helping maintainers port their C extensions.


What Changed Between 3.13 and 3.14

Python 3.13 introduced the free-threaded build as experimental. It worked, but with caveats — roughly 40% single-threaded overhead, limited package compatibility, and no official support guarantees.

Python 3.14 is a different story:

Single-threaded overhead dropped from ~40% to 5-10%. The specializing adaptive interpreter (PEP 659) was re-enabled for free-threaded mode, and numerous optimizations across the interpreter brought the penalty down dramatically. On the pyperformance benchmark suite, overhead ranges from about 1% on macOS aarch64 to 8% on x86-64 Linux.

The PEP 703 implementation is complete. All the C API changes described in the PEP are finished. Temporary workarounds in the 3.13 interpreter were replaced with permanent solutions.

PEP 779 made it official. The Steering Council accepted PEP 779 in June 2025, removing the "experimental" label. This means the free-threaded build gets the same stability guarantees and bug-fix support as the default build.


The Benchmarks That Matter

The headline number everyone cites — "4x speedup!" — needs context. Here's what actual benchmarks show:

BenchmarkStandard PythonFree-Threaded (4 threads)Speedup
Prime number computation3.70s0.35s~10x
Fibonacci (4 threads)25-33s9.3s~3x
File I/O (20 files, thread pool)18.77s5.13s~3.6x
Matrix multiplication (multiprocessing baseline)4.49s6.29s0.7x (slower)

That last row is important. Free-threaded Python is not faster for everything. CPU-bound tasks that are already optimized for multiprocessing can actually be slower in free-threaded mode because of the per-object locking overhead that replaces the GIL. The GIL was coarse-grained but cheap. Per-object locks are fine-grained but have overhead.

The sweet spot for free-threaded Python is workloads where:

  • You have multiple CPU-bound tasks that can run in parallel
  • The tasks don't heavily share mutable state
  • The inter-thread communication overhead of multiprocessing was your bottleneck
  • You need shared memory access between parallel tasks

For I/O-bound web applications, the story is more nuanced. A Django benchmark with free-threaded Python 3.14 showed approximately 2x throughput improvement for request handling — real, but not transformative for most web apps that are already I/O-bound and scale fine with async.


Ecosystem Readiness: The 51% Mark

A year ago, when Python 3.13 shipped, the ecosystem was "almost completely broken" on the free-threaded build. Most pip install commands failed on anything beyond the simplest packages.

As of early 2026, 183 out of the 360 most-downloaded packages that ship native wheels have free-threaded wheels on PyPI — approximately 51% compatibility.

The critical packages are mostly ready:

PackageFree-Threaded StatusNotes
NumPySupported since 2.1Some threading bottlenecks being addressed
SciPyIn progressExtension modules being ported
pandasIn progressWork ongoing
scikit-learnIn progressPart of Quansight effort
MatplotlibIn progressPart of Quansight effort
Cython3.1.0+ supportedfreethreading_compatible directive
pybind112.13.1+ supportedgil_not_used argument
PyO3 (Rust)SupportedRust bindings work well
PillowSupportedWheels available
PyYAMLSupportedWheels available
SQLAlchemyIn progressWork ongoing

The Python Free-Threading Guide maintains an up-to-date compatibility tracker. The Quansight-Labs GitHub repository also tracks progress for scientific and ML packages specifically.

Cython 3.1.0 was a turning point. Before that release, any package using Cython extensions couldn't build on the free-threaded interpreter. Now Cython provides a freethreading_compatible compiler directive, and extensions can opt in to GIL-free operation on a per-module basis.


How to Actually Use It

Installation

With pyenv:

# Install the free-threaded build
pyenv install 3.14t-dev
pyenv local 3.14t-dev

From source:

./configure --disable-gil --enable-optimizations
make -j$(nproc)
make install

Verify it's working:

import sysconfig
print(sysconfig.get_config_var("Py_GIL_DISABLED"))
# Should print: 1

import sys
print(sys._is_gil_enabled())
# Should print: False

Your First Free-Threaded Program

import threading
import time

def cpu_work(n):
    """Compute sum of squares — pure CPU work."""
    total = 0
    for i in range(n):
        total += i * i
    return total

# Run 4 CPU-bound tasks in parallel
start = time.time()
threads = []
for _ in range(4):
    t = threading.Thread(target=cpu_work, args=(10_000_000,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

elapsed = time.time() - start
print(f"Completed in {elapsed:.2f}s")
# Standard Python: ~12s (threads run sequentially due to GIL)
# Free-threaded:   ~3s  (threads run in parallel on 4 cores)

Installing Packages

Most packages install normally:

pip install numpy pandas requests

For packages without free-threaded wheels, pip will try to build from source. If the package's C extensions aren't compatible, the build may fail. Check the compatibility tracker before relying on a package.


When Free-Threaded Python Makes Sense (And When It Doesn't)

WorkloadUse Free-Threaded?Why
CPU-bound parallel computationYesTrue parallelism across cores
Data processing pipelineYesShared memory, no IPC overhead
Web server (I/O-bound)MaybeMarginal gains over async/multiprocessing
NumPy-heavy computationNot yetNumPy already releases GIL for C operations
Single-threaded scriptsNo5-10% slower for no benefit
C extension-heavy codeCheck firstDepends on extension compatibility
ML training with PyTorch/TFNoThese frameworks manage their own parallelism

The key insight: if you're already using multiprocessing and it's working fine, there's no urgent reason to switch. Free-threaded Python is most compelling when:

  1. You need shared memory between parallel workers. multiprocessing requires pickling data across process boundaries. Threads share the same memory space — no serialization overhead.

  2. Your parallelism is fine-grained. Spawning a process has startup cost. Threads are lightweight. If you have thousands of small parallel tasks, threads win.

  3. You're building concurrent systems (not just parallel computation). Event loops, producer-consumer patterns, and shared-state coordination are natural with threads, awkward with processes.


The C Extension Challenge

The biggest risk with free-threaded Python isn't Python code — it's C extensions.

Under the GIL, C extension authors could assume that only one thread was executing Python code at a time. Many extensions aren't thread-safe because they never needed to be. With the GIL removed, those assumptions break.

The porting guide covers the technical details, but here's the summary:

Cython extensions: Use the freethreading_compatible directive in Cython 3.1.0+. This tells the interpreter your extension is safe to use without the GIL.

pybind11 extensions: Use the gil_not_used argument to create_extension_module() in pybind11 2.13.1+.

Raw C extensions: Mark your module as supporting free-threading by setting Py_mod_gil to Py_MOD_GIL_NOT_USED in your module definition.

If an extension doesn't declare free-threading support, CPython will re-enable the GIL for the entire process when that module is imported. This is the safety net: incompatible extensions can't cause data races because the GIL will protect them. But it also means one unported extension will negate free-threading for your entire application.

You can check the GIL status at runtime:

import sys
print(sys._is_gil_enabled())  # True if any module forced the GIL back on

Django, Flask, and Web Frameworks

The question everyone asks: can I run my web app with free-threaded Python?

Django is actively exploring free-threaded support. There was a DjangoCon US 2025 talk specifically about free-threaded Django. The key finding: Django's ORM and request handling work in free-threaded mode, but the question of whether to recommend it for production is still being evaluated. A benchmark showed approximately 2x throughput improvement for ASGI request handling.

Application servers are evolving too. Granian, a newer Python application server written in Rust, provides explicit free-threaded support. Gunicorn and Uvicorn are being examined for compatibility, but neither has officially declared free-threaded support yet.

For most web applications in 2026, the practical recommendation is: if you're running ASGI with an async framework and your deployment already uses multiple worker processes, the gains from free-threading will be modest. The biggest beneficiaries are synchronous Django/Flask applications running with thread-based workers, where free-threading means actual parallelism instead of GIL-serialized pseudo-parallelism.


What I Actually Think

The GIL isn't dead. It's optional. And that distinction matters more than the headlines suggest.

Here's the reality: the default Python 3.14 build still has the GIL. You have to explicitly install or build the free-threaded variant. Most packages are still catching up — 51% compatibility is progress, not parity. Web frameworks are cautiously optimistic but not recommending production use yet. And the 5-10% single-threaded overhead means you're paying a cost even when you're not using threads.

But none of that should obscure how significant this is.

Python's GIL has been the single biggest complaint about the language for two decades. It's the reason every "Python vs. Go" or "Python vs. Rust" comparison includes the paragraph about concurrency. It's the reason data scientists write multiprocessing code with shared-memory hacks instead of simple threads. It's the reason Python web servers run multiple processes instead of multiple threads.

The free-threaded build isn't perfect today. But the trajectory is clear: single-threaded overhead dropped from 40% (3.13) to 5-10% (3.14). Package compatibility went from "almost completely broken" to 51% in one year. Every major framework and build tool is adding support. Phase III — making free-threading the default — isn't a question of if, but when.

I think the practical inflection point will be Python 3.15 or 3.16. By then, the long tail of C extension packages will be ported, the single-threaded overhead will be further reduced, and web frameworks will have battle-tested their free-threaded code paths. That's when you'll see widespread production adoption.

But if you're starting a new CPU-bound Python project today — data processing, scientific computing, image pipelines — I'd start with the free-threaded build. The ecosystem for numerical work (NumPy, SciPy) is far enough along. The performance gains are real. And building for threads from the start is easier than migrating from multiprocessing later.

Sam Gross's work will be remembered as one of the most consequential contributions to CPython. He didn't just remove a lock. He proved it was possible to do so without breaking the language. The rest is just engineering.


Sources

  1. PEP 779 — Criteria for Supported Status for Free-Threaded Python
  2. PEP 703 — Making the Global Interpreter Lock Optional in CPython
  3. What's New in Python 3.14 — Official Documentation
  4. Python Support for Free Threading — Official How-To
  5. Python Free-Threading Guide
  6. Compatibility Status Tracking — Free-Threading Guide
  7. The First Year of Free-Threaded Python — Quansight Labs
  8. Enhancing the Python Ecosystem with Free Threading — Meta Engineering
  9. Faster Python: Unlocking the GIL — JetBrains PyCharm Blog
  10. Python 3.14 Free-Threading: True Parallelism — Edgar Montano
  11. Python 3.14: Is It Fast? — Miguel Grinberg
  12. Python 3.14 and the End of the GIL — Towards Data Science
  13. State of Python 3.13 Performance: Free-Threading — CodSpeed
  14. Python 3.14 and Django: 2x Throughput — Medium
  15. Free-Threaded Django — DjangoCon US 2025
  16. Python Language Summit 2023: Making the GIL Optional — PSF
  17. Installing Free-Threaded CPython — Guide
  18. Updating Extension Modules for Free-Threading — Guide
  19. Quansight-Labs Free-Threaded Compatibility Tracker — GitHub
  20. NumPy Thread Safety — NumPy 2.1 Manual
  21. Python 3.14 — Astral Blog
  22. PEP 779 Hacker News Discussion