Free-threaded Python – hit or miss?¶

Python 3.14 will be released in a few weeks with some important changes regarding parallelism:
Both features represent advances in the use of Python for executing parallel code. But are they threatened with a marginal existence similar to asyncio?
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 will probably always remain synchronous.
SQLAlchemy 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 also 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
Does free-threaded mode make asyncio
more useful or redundant?¶
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. In Python 3.14, this now seems to be looking better, and we are now wondering whether we should introduce ‘free-threaded’ in some well-tested projects.
However, supporting both synchronous and asynchronous APIs remains a major challenge. We should also continue to
be selective about where asynchrony should be supported. For example, most of
the Python standard library does not natively support asynchrony. The dunder
methods and __init__
cannot be asynchronous either, so asynchronous
network requests cannot be used there.
It may also be necessary to fragment the backend for synchronous and asynchronous operations:
requests
andhttpx
are suitable backends for synchronous HTTP calls.aiohttp
andhttpx
are suitable for asynchronous operations.
Since none of these backends are part of the standard Python library, their
introduction and support for the most important CPython platforms are not
synchronised. For example, aiohttp
currently has neither Python 3.14 wheels
nor free-threaded support.
Testing your asynchronous code requires different mocks, different calls and, in the case of pytest, a whole range of extensions and patterns for fixtures. This usually becomes very confusing.
See also
async test patterns for Pytest by Anthony Shaw
Summary¶
The use cases for asyncio
remain very limited, and its popularity is
therefore very restricted. However, the popularity of the asynchronous web
framework FastAPI has
continued to grow and currently records over 100 million downloads per month.
Considering that asynchronous processes are predominantly used for HTTP and
network access, this is more of a success for uvloop, an alternative implementation of the
event loop used by FastAPI. It remains to be seen whether Python 3.14, with its
support for sub-interpreters and free-threading functions, will make more
parallel and concurrent use cases practical and useful.