Maison > Article > développement back-end > Programmation asynchrone avec Asyncio
Dans le monde de la programmation, le concept de « non-blocage » est omniprésent. Les développeurs JavaScript utilisent souvent le terme « asynchrone » car c'est l'un des points forts de JavaScript. Cependant, pour vraiment comprendre la programmation asynchrone, il est essentiel de saisir les concepts de programmation concurrente et parallèle.
Lorsque plusieurs entités indépendantes travaillent simultanément, la programmation est concurrente. Cela ne signifie pas nécessairement que ces tâches s’exécutent exactement au même moment. Au lieu de cela, cela signifie que les tâches progressent au fil du temps en partageant des ressources, telles que le temps CPU. Le principal avantage de la programmation concurrente est sa robustesse : si un processus plante, le reste de votre programme continue de fonctionner.
Si un algorithme peut diviser son travail en plusieurs parties, il est parallèle. Plus vous disposez de processeurs, plus vous bénéficiez du parallélisme. Une programmation parallèle efficace optimise les ressources des machines modernes pour de meilleures performances.
Exemple de concurrence :
Imaginez que vous préparez un repas où vous devez griller de la viande et préparer une sauce. Vous commencez par mettre la viande sur le barbecue. Pendant que la viande grille, vous hachez les tomates et autres légumes pour la sauce. Ensuite, vous commencez à faire bouillir la sauce tout en vérifiant de temps en temps la viande. Ici, les deux tâches (griller la viande et préparer la sauce) sont en cours, mais vous basculez votre attention entre elles. Cela représente la concurrence.
Exemple de parallélisme :
Maintenant, disons que vous avez un ami pour vous aider. Pendant que vous vous concentrez sur la grillade de la viande, votre ami s'occupe de préparer la sauce. Les deux tâches sont effectuées simultanément sans qu’il soit nécessaire de basculer l’attention de l’une à l’autre. Cela représente le parallélisme.
La programmation asynchrone implique la gestion des opérations d'entrée/sortie (E/S) qui se produisent en dehors de votre programme, telles que la saisie utilisateur, l'impression sur un terminal, la lecture à partir d'un socket ou l'écriture sur le disque. Les principales caractéristiques des E/S asynchrones sont :
Le temps nécessaire à l'opération ne dépend pas du processeur. Au lieu de cela, cela dépend de facteurs tels que la vitesse du disque, la latence du réseau et d'autres conditions externes.
Le programme ne peut pas prédire quand l'opération prendra fin.
Pour les services avec des E/S importantes (comme les serveurs Web, les bases de données et les scripts de déploiement), l'optimisation de ces opérations peut grandement améliorer les performances.
Voyons des exemples de code bloquant et de code non bloquant.
Considérez un programme simple :
import time def task(): time.sleep(2) print("Hello") for _ in range(3): task()
Dans ce programme synchrone, chaque tâche attend la fin de la précédente, provoquant des retards.
Maintenant, regardons une version asynchrone utilisant asyncio :
import asyncio async def task(): await asyncio.sleep(2) print("Hello") async def main(): tasks = [task() for _ in range(3)] await asyncio.gather(*tasks) asyncio.run(main())
Dans ce programme asynchrone, les tâches s'exécutent simultanément, réduisant ainsi le temps total d'exécution. Explorons les composants de la programmation asynchrone.
Les boucles d'événements, les coroutines et les futurs sont les éléments essentiels d'un programme Python asynchrone.
Boucle d'événements : Gère le flux de commutation et d'exécution des tâches, en gardant une trace des tâches à exécuter de manière asynchrone.
Coroutines : Fonctions spéciales qui peuvent être mises en pause et reprises, permettant à d'autres tâches de s'exécuter pendant l'attente. Une coroutine spécifie où dans la fonction l'événement de changement de tâche doit avoir lieu, rendant le contrôle à la boucle d'événements. Les coroutines sont généralement créées par la boucle d'événements et stockées en interne dans une file d'attente de tâches.
Futures : Espaces réservés pour les résultats des coroutines, stockant le résultat ou les exceptions. Dès que la boucle d'événements initie une coroutine, un futur correspondant est créé qui stocke le résultat de la coroutine, ou une exception si une exception a été lancée lors de l'exécution de la coroutine.
Une fois expliquées les parties cruciales de la programmation asynchrone en Python, écrivons du code.
Maintenant que vous comprenez le modèle de programmation asynchrone, écrivons un petit script et analysons l'exécution. Voici un simple script asynchrone :
import asyncio async def task(): await asyncio.sleep(2) print("Hello") async def main(): tasks = [task() for _ in range(3)] await asyncio.gather(*tasks) asyncio.run(main())
Dans le code ci-dessus, nous essayons de continuer l'exécution d'autres tâches même si une autre en cours d'exécution est en veille (bloquante). Notez le mot-clé async devant la tâche et les fonctions principales.
Ces fonctions sont désormais des coroutines.
Coroutines functions in Python are preceded by the keyword async. The main() function here is the task coordinator or our single event loop, as it executes all tasks using the async.gather method. The asyncio.gather function runs awaitable objects concurrently.
Output:
Hello Hello Hello Program executed in 2.01 seconds.
When each task reaches await asyncio.sleep(2), it simply goes to the next task and comes back when it's finished. It's like saying, "I am going to sleep for 2 seconds. Do something else."
Let's see the synchronous version for a quick comparison.
import time def task(): time.sleep(2) print("Hello") for _ in range(3): task()
In the code above, we are going the traditional programming way in Python. You will notice that the execution of the process will take much more time.
Output:
Hello Hello Hello Program executed in 6.01 seconds.
Now you can notice the execution time. Think of time.sleep() as a blocking task and asyncio.sleep() as a non-blocking or long task. In asynchronous programming, the benefit of awaiting something, like asyncio.sleep(), is that the surrounding function can temporarily cede control to another function that is ready to execute immediately.
With some basic examples of asynchronous programming in Python understood, let's explore the rules of asynchronous programming in Python.
Coroutines: Coroutines cannot be executed directly. If you try to run a coroutine function directly, it returns a coroutine object. Instead, use asyncio.run():
import asyncio async def hello(): await asyncio.sleep(1) print('Hello') asyncio.run(hello())
Awaitable Objects: Coroutines, futures, and tasks are the main awaitable objects. Python coroutines are awaitables and can be awaited from other coroutines.
Await Keyword:await can only be used within async functions.
async def hello(): await asyncio.sleep(1) print("Hello")
Compatibility: Not all Python modules are compatible with asynchronous programming. For example, replacing await asyncio.sleep() with time.sleep() will cause an error. You can check the list of compatible and maintained modules here.
In the next section, we will explore a common use of asynchronous programming, HTTP requests.
Let's take a look at the following piece of code:
import aiohttp import asyncio async def fetch(session, city): url = f"https://www.prevision-meteo.ch/services/json/{city}" async with session.get(url) as response: data = await response.json() print(f"Temperature at {city}: {data['current_condition']['tmp']} C") async def main(): async with aiohttp.ClientSession() as session: cities = ['paris', 'toulouse', 'marseille'] tasks = [fetch(session, city) for city in cities] await asyncio.gather(*tasks) asyncio.run(main())
In the code above, we create two asynchronous functions: one to fetch data from the prevision-meteo URL and a main function to execute the processes in the Python code. The goal is to send asynchronous HTTP GET requests to retrieve temperatures and print the responses.
In the main and fetch functions, we use async with. In the fetch function, async with ensures that the connection is closed properly. In the main function, it ensures that the ClientSession is closed after completing the requests. These practices are important in asynchronous coding in Python to manage resources efficiently and prevent leaks.
In the last line of the main function, we use await asyncio.gather(*tasks). In our case, it runs all tasks concurrently, allowing the program to send multiple HTTP requests simultaneously. Using await ensures that the program waits for all tasks to complete before proceeding.
Output:
Temperature at marseille: 25 C Temperature at toulouse: 24 C Temperature at paris: 18 C Program executed in 5.86 seconds.
Code:
import requests import time def fetch(city): url = f"https://www.prevision-meteo.ch/services/json/{city}" response = requests.get(url) data = response.json() print(f"Temperature at {city}: {data['current_condition']['tmp']} C") def main(): cities = ['paris', 'toulouse', 'marseille'] for city in cities: fetch(city) start_time = time.time() main() print(f"Program executed in {time.time() - start_time:.2f} seconds.")
Output:
Temperature at Paris: 18 C Temperature at Toulouse: 24 C Temperature at Marseille: 25 C Program executed in 9.01 seconds.
The asynchronous model performs best when:
There are a large number of tasks, ensuring at least one task can always progress.
Tasks involve significant I/O, causing an asynchronous program to waste lots of time blocking when other tasks could be running.
Tasks are largely independent, minimizing inter-task communication (and thus for one task to wait upon another).
In this tutorial, we covered:
The concepts of asynchronous programming and related concepts.
Effective use of async/await.
Making asynchronous HTTP requests with aiohttp.
The benefits of asynchronous programming.
Thanks for reading. The second part will cover asynchronous programming with Django.
Python Documentation: Coroutines and Tasks
Python Documentation: asyncio - Asynchronous I/O
aiohttp Documentation
Aio Libraries
Concurrency vs Parallelism
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!