Le GIL libérait un thread pendant l'attente. asyncio pousse l'idée plus loin : un seul thread suffit.The GIL freed a thread during the wait. asyncio pushes the idea further: a single thread is enough.
Le numéro précédent a montré que le threading accélère l'I/O parce que le GIL se relâche pendant l'attente d'une réponse réseau. Mais ouvrir un thread par requête a un coût — mémoire, création, bascule gérée par l'OS — qui devient prohibitif à mesure que le nombre de tâches en attente grandit. asyncio pose une question différente : si le seul moment utile est celui où une tâche redevient active, pourquoi ouvrir un thread du tout ? Une coroutine occupe une fraction de la mémoire d'un thread, et sa bascule est gérée entièrement en Python, par une boucle d'événements, dans un unique thread. Ce numéro explique comment cette coopération fonctionne — et son prix.The previous issue showed that threading speeds up I/O because the GIL releases during a network response wait. But opening a thread per request has a cost — memory, creation, OS-managed switching — that becomes prohibitive as the number of waiting tasks grows. asyncio asks a different question: if the only useful moment is when a task becomes active again, why open a thread at all? A coroutine takes a fraction of a thread's memory, and its switching is handled entirely in Python, by an event loop, within a single thread. This issue explains how that cooperation works — and its price.
# n°22 : un thread par requête -- l'OS bascule entre eux import threading def telecharger_thread(url): ... threads = [threading.Thread(target=telecharger_thread, args=(u,)) for u in urls] # 1000 urls -- 1000 threads -- coût mémoire et coût de bascule réels # ici : une seule coroutine par requête -- asyncio bascule entre elles, # dans UN SEUL thread, sans jamais demander à l'OS de changer de thread import asyncio # session : un client HTTP asynchrone, par exemple aiohttp.ClientSession() async def telecharger(url): async with session.get(url) as resp: return await resp.text() async def main(): taches = [telecharger(u) for u in urls] return await asyncio.gather(*taches) # 1000 urls -- 1000 coroutines -- un seul thread, une seule boucle
Un thread peut être interrompu à tout moment par l'OS, sans son accord. Une coroutine ne rend la main que là où elle contient await, et nulle part ailleurs — la coopération est explicite, écrite dans le code, jamais imposée de l'extérieur.A thread can be interrupted by the OS at any moment, without its consent. A coroutine only hands back control where it contains await, and nowhere else — the cooperation is explicit, written into the code, never imposed from outside.