Free-threaded Python – hit or miss?¶

Python 3.14 will be released in a few weeks with some important changes regarding concurrency:
Both features represent advances in the use of Python for concurrent and parallel code execution. But are they threatened with a marginal existence similar to asyncio?
Why did asyncio
fail?¶
The typical use case for asyncio
is web development. Coroutines are well
suited for process-independent network calls such as HTTP requests and database
queries. Why should the entire Python interpreter be blocked while waiting for a
query to be executed on another server? And yet, popular frameworks still do not
support asyncio
:
Django only partially supports
asyncio
; for example, the ORM is ‘still working on async support’.Flask’s
asyncio
support is not very powerful, see Using async and await.SQLAlchemy only gained
asyncio
support in 2023, see What’s New in SQLAlchemy 1.4?In the Python for Data Science tutorial,
asyncio
is only recommended as a last resort for performance optimisation.See also
This has to do with some known issues with asyncio
:
asyncio
does not support asynchronous operations on the file system. Even if files are opened withO_NONBLOCK
, read and write operations are blocked. One solution to this is to use theaiofiles
package, which provides asynchronous file functions.asyncio
is not intuitive. For example, what does it mean when code is ‘blocked’, and when is it necessary to switch to threads? Without this basic knowledge, asynchronous code will behave incorrectly, but will not break. Developers therefore do not get the quick feedback they expect from Python.‘An event loop runs in a thread (typically the main thread) and executes all callbacks and Tasks in its thread. While a Task is running in the event loop, no other Tasks can run in the same thread. When a Task executes an await expression, the running Task gets suspended, and the event loop executes the next Task.’
Source: Concurrency and Multithreading
Supporting both synchronous and asynchronous APIs is challenging. The backend may need to be fragmented for synchronous and asynchronous operations. For asynchronous operations,
aiohttp
orhttpx
can be used for this purpose.Finally, testing your asynchronous code also requires various mocks, various calls and, in the case of pytest, a whole range of extensions and patterns for fixtures. This can quickly become confusing.
See also
async test patterns for Pytest by Anthony Shaw
So it’s not surprising that asyncio
can’t even hold its own for asynchronous
network access. Instead, the Python web framework
FastAPI has become very
popular for this purpose, using an alternative implementation of the event loop
called uvloop.
The use cases for asyncio
will therefore remain minimal and its distribution
very limited.
Does free-threaded Python make it better?¶
Python 3.13 introduced a ‘free-threaded’ version of Python, in which the
GIL was removed and replaced with smaller,
more granular locks. However, the 3.13 version proved to be insufficiently
stable for productive use. This looks better in Python 3.14. In Python 3.14t,
the InterpreterPoolExecutor and free-threading
features will make more parallel and
concurrent use cases feasible and faster. In the following example, up to eight
CPUs can be used simultaneously with the multithreaded
argument:
$ uv run python -X gil=1 parse.py
Using single thread for parsing news
Parsing speed: 15 news/sec
$ uv run python -X gil=1 parse.py --multithreaded
Using multithreading for parsing news
Parsing speed: 37 news/sec
$ uv run python -X gil=0 parse.py --multithreaded
Using multithreading for parsing news
Parsing speed: 73 news/sec
Free-threaded Python also eliminates many of the problems with asyncio
that
I discussed in this article. In addition, a number of Python libraries now
support free-threaded Python, including:
Library |
Version |
---|---|
argon2-cffi-bindings |
≥ 25.1.0 |
CFFI |
≥ 2.0.0 |
cibuildwheel |
≥ 2.19 |
Cython |
≥ 3.1.0 |
hypothesis |
≥ 6.135.32 |
joblib |
≥ 1.4.2 |
matplotlib |
≥ 3.9.0 |
maturin |
≥ 1.7.5 |
meson-python |
≥ 0.16.0 |
NumPy |
≥ 2.1.0 |
pandas |
≥ 2.2.3 |
Pillow |
≥ 11.0.0 |
pydantic |
≥ 2.11.0 |
pybind11 |
≥ 2.13 |
PyO3 |
≥ 0.23 |
PyTorch |
≥ 2.6.0 |
scikit-learn |
≥ 1.6.0 |
SciPy |
≥ 1.15.0 |
Shapely |
≥ 2.1.0 |
tox |
≥ 4.26.0 |
uv |
≥ 0.4.24 |
So we will now try out free-threaded Python in some of our well-tested projects.