12 points par darjeeling 2025-09-02 | 9 commentaires | Partager sur WhatsApp

Python Async, pourquoi n’est-ce pas encore grand public ?

asyncio de Python est un outil puissant qui peut réduire les temps d’attente et améliorer de façon spectaculaire l’efficacité d’un programme dans les environnements riches en opérations d’I/O (entrées/sorties). Mais malgré ses nombreux avantages, tous les développeurs Python ne l’utilisent pas activement, et cela s’explique par plusieurs raisons fondamentales.


1. Ça complique tout dans la tête : la charge cognitive

Le principal obstacle, c’est la complexité. Le code synchrone est intuitif, car on peut suivre son flux d’exécution de haut en bas, dans l’ordre, comme lorsqu’on lit un livre.

Le code asynchrone, lui, fonctionne autrement. C’est un peu comme un cuisinier qui, pendant que les pâtes cuisent pour préparer un plat (temps d’attente), prépare la sauce d’un côté et, dans les moments restants, découpe les légumes. Au final, le repas peut être prêt plus vite, mais dans la tête du cuisinier, il faut sans cesse surveiller l’état de plusieurs tâches : « les pâtes sont-elles assez cuites ? », « la sauce ne brûle-t-elle pas ? », etc.

Comme le flux d’exécution du code passe ainsi d’un endroit à l’autre, il devient difficile de savoir quelle tâche est en cours et laquelle est en attente. Cela rend le débogage particulièrement délicat lorsqu’un bug survient et qu’il faut en retrouver la cause.


2. Un écosystème éclaté : la compatibilité des bibliothèques

Un autre problème de Python est que l’écosystème des bibliothèques est scindé entre « synchrone » et « asynchrone ». De nombreuses bibliothèques célèbres et pratiques (par exemple requests, la bibliothèque standard de fait pour les requêtes HTTP, ou beaucoup d’ORM) ne fonctionnent qu’en mode synchrone.

Si l’on utilise mal une bibliothèque synchrone dans du code asynchrone, le plus grand atout de l’asynchrone — la « boucle d’événements » — se retrouve complètement bloqué pendant l’exécution de ce code synchrone. C’est comme aménager plusieurs voies de circulation pour n’en utiliser qu’une seule : l’intérêt de l’asynchrone disparaît. Pour résoudre ce problème, il faut apprendre et adopter d’autres bibliothèques compatibles asynchrone (aiohttp, asyncpg, etc.), ce qui rend la courbe d’apprentissage encore plus raide.


3. Une seule cible à la fois : le GIL (verrou global de l’interpréteur)

Le GIL (Global Interpreter Lock) est l’une des caractéristiques historiques de Python : au sein d’un même processus, même s’il existe plusieurs threads, un seul thread peut s’exécuter à la fois. asyncio fonctionne sur un seul thread, il n’entre donc pas directement en conflit avec le GIL, mais l’existence du GIL limite malgré tout le champ d’application de async.

asyncio est optimisé pour exploiter les temps d’attente liés aux I/O (attente d’une réponse réseau, attente de lecture de fichier, etc.). Mais si une tâche gourmande en CPU avec des calculs très complexes est incluse dans une fonction async, toute la boucle d’événements s’arrête jusqu’à la fin de ce calcul. Pendant ce temps, les autres opérations d’I/O n’ont d’autre choix que d’attendre. En fin de compte, pour les tâches qui nécessitent un véritable traitement parallèle, il reste nécessaire d’utiliser d’autres techniques comme multiprocessing.


4. Un espoir pour l’avenir : Python 3.14 et la suppression du GIL

Mais il existe une nouvelle très encourageante face à ces limites. À partir de Python 3.13, une suppression optionnelle du GIL a été introduite à titre expérimental, et elle devrait gagner en maturité avec la version 3.14.

Porté par la proposition PEP 703, ce changement vise à permettre, si le développeur le souhaite, l’exécution de code Python sans GIL. Si cela devient réalité, Python pourra enfin tirer parti d’un véritable multithreading, avec plusieurs threads utilisant simultanément plusieurs cœurs CPU.

Combiné à asyncio, cela pourrait produire une synergie considérable. Les tâches d’I/O seraient traitées efficacement avec asyncio, tandis que les tâches CPU très intensives pourraient être déléguées à des threads séparés pour être exécutées en parallèle, sans les contraintes du GIL. Ce changement est considéré comme un tournant majeur pour l’écosystème Python et suscite de grands espoirs : il pourrait faire tomber nombre des barrières qui freinaient jusqu’ici l’adoption de la programmation async.

9 commentaires

 
barca105 2025-09-03

J’ai l’impression que le GIL arrive un peu de nulle part ici… Même si le GIL était supprimé,
si l’on veut utiliser le multithreading à la fois pour les charges I/O bound et CPU bound,
il vaudrait peut-être mieux adopter une autre alternative que Python…

J’ai aussi l’impression qu’asyncio suscite un rejet assez marqué chez ceux qui pratiquent Python en profondeur.
Il me semble avoir souvent entendu l’avis selon lequel gevent aurait dû devenir le courant dominant.

 
sonnet 2025-09-03

Je suis d’accord sur le fait qu’on ne peut pas s’attendre à ce que l’orientation actuelle autour du GIL soit à la hauteur des « autres alternatives ».
Mais dire qu’il faut adopter d’autres alternatives que Python ne devrait-il pas conduire non pas à soutenir qu’il n’y a pas de problème, mais au contraire à reconnaître qu’il y a bien un problème ?

 
jasonroh123 2025-09-02

J’utilise beaucoup asyncio... c’est tout à fait utilisable. Il y a une limite liée au fait que l’annulation des tâches est edge-triggered (et non level-triggered), mais en réalité on écrit rarement du code qui prend vraiment en compte l’annulation des tâches tout en gérant cela proprement. Un problème plus important, c’est que l’event loop ne conserve qu’une weak reference vers les tasks, donc elles peuvent disparaître à cause du GC... mais ça se règle avec la structured concurrency.

Pour la plupart des grandes opérations d’I/O, ce n’est pas difficile de trouver des bibliothèques qui prennent en charge asyncio...

Le GIL ? Ça n’a pas grand-chose à voir. L’idée même d’utiliser asyncio pour paralléliser des tâches CPU intensive est un peu étrange. Si le GIL s’améliore, ce sera utile pour le multithreading sur des charges CPU intensive. L’async, lui, sert à faire tourner le plus efficacement possible les portions limitées par l’I/O...

Bref, conclusion : il y a bien quelques problèmes de conception, mais pour atteindre l’objectif, je l’utilise sans difficulté particulière et ça fonctionne très bien en production.

 
maitrouble 2025-09-05

Avez-vous déjà vu des tâches être récupérées par le GC ?

 
sonnet 2025-09-03

Bien sûr, moi aussi j’utilise .asyncio jusqu’à saturation en production, mais je ne suis pas assez satisfait de l’expérience actuelle pour dire que « je l’utilise bien ».

 
sonnet 2025-09-03

L’asyncio actuel est conçu en partant du principe du GIL et constitue, d’une certaine manière, une stratégie pour le contourner, donc le GIL n’interagit pas directement avec asyncio.

Mais si l’on considère l’ensemble de la programmation concurrente fondée sur asyncio, je pense qu’affirmer que le GIL n’a aucun rapport revient à dire quelque chose comme : « C’est Python, donc c’est normal que ça ne marche pas. »

 
doolayer 2025-09-02

Je vais simplement utiliser joblib.

 
sonnet 2025-09-02

Le problème d’Asyncio, ce n’est pas la difficulté de la programmation asynchrone, déjà complexe en soi, mais sa piètre qualité. Une conception qui sacrifie la cohérence et l’universalité n’est pas si rare en Python, mais avec quelque chose comme ProactorEventLoop, on se retrouve encore aujourd’hui avec des bugs signalés il y a 5 ans qui provoquent des interruptions de service.

Quand on est dans la position de devoir l’utiliser de force, ce genre d’article est franchement difficile à prendre à la légère.

 
sonnet 2025-09-02

Bien sûr, une raison plus importante est peut-être que, à cause du GIL, les gains qu’on peut obtenir au départ sont plus limités que dans d’autres environnements.
Je pense qu’affirmer qu’on peut créer une synergie s’il n’y a pas de GIL relève presque de la tromperie. Si l’on donne une prothèse, certes utile malgré l’inconfort, à un coureur à qui il manque une jambe, est-ce que c’est vraiment une « synergie » ?