Asynchronous en Python
Publié le 26 août 2025
Au début du développement d’un programme, automatiser l’exécution de tâches par lots semble toujours plus efficace que de les déclencher manuellement. Mais lorsque les projets atteignent une certaine maturité, les besoins de **rapidité** se font ressentir. C’est ce que j’ai expérimenté récemment en développant un programme de **market making sur options** : l’exécution devient alors critique !
Par défaut, un programme Python est exécuté de façon **linéaire** : chaque étape doit se terminer avant que la suivante ne commence. Mais si certaines tâches sont indépendantes, comment augmenter l’efficacité ?
Prenons un exemple du quotidien — la cuisine 🍳. En tant que débutant à l’étranger, je devais apprendre à cuisiner moi-même. Et j’ai vite compris que je ne pouvais pas tout faire en même temps sans m’embrouiller. Cela illustre parfaitement les différents modes d’exécution en programmation.
Lv 1 Cuisinier débutant : (Single thread)
- On traite une tâche après l’autre : laver les légumes, puis faire bouillir l’eau, puis les plonger.
- Chaque étape est indépendante et exécutée dans l’ordre.
Lv 2 Cuisinier intermédiaire : (Asynchronous)
- On identifie les tâches longues mais qui ne nécessitent pas de surveillance (ex : cuisson du riz dans un cuiseur électrique).
- Pendant que le riz cuit, on prépare autre chose (œuf au plat, ingrédients, etc.).
Lv 3 Cuisinier confirmé : (Multi-threads + Async)
- On distingue bien les tâches longues.
- On peut exécuter plusieurs tâches en parallèle (faire bouillir les pâtes tout en cuisant les œufs), mais avec plusieurs ustensiles.
😅 Ce sont littéralement les leçons que j’ai tirées en cuisinant !
En Python, appliquons ce raisonnement : préparer du café (3 minutes) et griller un bagel (5 minutes). Si on les exécute séquentiellement, cela prend 8 minutes. Mais en parallèle, on peut optimiser !
Exemple classique (sans Async)
import time
def brew_coffee():
print("Start brew coffee")
time.sleep(3 * 60)
print("End brew coffee")
return "coffee ready"
def toast_bagel():
print("Start toast bagel")
time.sleep(5 * 60)
print("End toast bagel")
return "bagel toasted"
def main():
start_time = time.time()
result_coffee = brew_coffee()
result_bagel = toast_bagel()
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Result of brew_coffee: {result_coffee}")
print(f"Result of toast bagel: {result_bagel}")
print(f"Total execution time: {elapsed_time:2f} seconds")
if __name__ == "__main__":
main()
La version suivante illustre l’approche **asynchrone** : pendant que la machine à café travaille, on lance le grille-pain, et les deux se terminent quasiment en même temps ⏱️.
Exemple avec Async
import asyncio
import time
async def brew_coffee():
print("Start brew coffee")
await asyncio.sleep(3 * 60)
print("End brew coffee")
return "coffee ready"
async def toast_bagel():
print("Start toast bagel")
await asyncio.sleep(5 * 60)
print("End toast bagel")
return "bagel toasted"
async def main():
start_time = time.time()
# méthode 1: asyncio.gather()
batch = asyncio.gather(brew_coffee(), toast_bagel())
result_coffee, result_bagel = await batch
# méthode 2: asyncio.create_task()
# coffee_task = asyncio.create_task(brew_coffee())
# toast_task = asyncio.create_task(toast_bagel())
# result_coffee = await coffee_task
# result_toast = await toast_task
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Result of brew_coffee: {result_coffee}")
print(f"Result of toast bagel: {result_bagel}")
print(f"Total execution time: {elapsed_time:2f} seconds")
if __name__ == "__main__":
asyncio.run(main())
Conclusion
L’asynchronisme est aujourd’hui une méthode courante pour accélérer des tâches gourmandes en temps, comme les opérations **I/O** (base de données, API, web scraping). La première étape est d’identifier quelles parties du projet sont lentes et peuvent être parallélisées.
D’autres solutions comme multiprocessing
et threading
existent pour le
traitement parallèle, mais elles consomment davantage de ressources CPU/mémoire
et peuvent entraîner des blocages si elles sont mal utilisées.
Pour ma part, j’utilise souvent une combinaison des deux approches.