"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Programmation asynchrone avec Asyncio

Programmation asynchrone avec Asyncio

Publié le 2024-08-21
Parcourir:389

Asynchronous Programming with 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.

Programmation simultanée

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 simultanée est sa robustesse : si un processus plante, le reste de votre programme continue de fonctionner.

Programmation parallèle

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.

Illustrer la concurrence et le parallélisme avec la cuisine

Exemple de concurrence :

Imaginez que vous préparez un repas dans lequel 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.

Qu’est-ce que la programmation asynchrone ?

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 se terminera.

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 considérablement améliorer les performances.

Voyons des exemples de code bloquant et de code non bloquant.

Exemple de code bloquant et non bloquant

Considérons 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.

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 elle a été levé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.

Écrire du code asynchrone

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.

Les fonctions coroutines en Python sont précédées du mot-clé async. La fonction main() ici est le coordinateur de tâches ou notre boucle d'événement unique, car elle exécute toutes les tâches à l'aide de la méthode async.gather. La fonction asyncio.gather exécute simultanément les objets attendus.

Sortir:

Hello
Hello
Hello
Program executed in 2.01 seconds.

Lorsque chaque tâche atteint wait asyncio.sleep(2), elle passe simplement à la tâche suivante et revient lorsqu'elle est terminée. C'est comme dire : "Je vais dormir 2 secondes. Fais autre chose."

Voyons la version synchrone pour une comparaison rapide.

import time

def task():
    time.sleep(2)
    print("Hello")

for _ in range(3):
    task()

Dans le code ci-dessus, nous suivons la méthode de programmation traditionnelle en Python. Vous remarquerez que l'exécution du processus prendra beaucoup plus de temps.

Sortir:

Hello
Hello
Hello
Program executed in 6.01 seconds.

Vous pouvez maintenant remarquer le temps d'exécution. Considérez time.sleep() comme une tâche bloquante et asyncio.sleep() comme une tâche non bloquante ou longue. Dans la programmation asynchrone, l'avantage d'attendre quelque chose, comme asyncio.sleep(), est que la fonction environnante peut temporairement céder le contrôle à une autre fonction prête à s'exécuter immédiatement.

Avec quelques exemples de base de programmation asynchrone en Python compris, explorons les règles de la programmation asynchrone en Python.

Règles de programmation Asyncio

  1. Coroutines : Les coroutines ne peuvent pas être exécutées directement. Si vous essayez d'exécuter directement une fonction coroutine, elle renvoie un objet coroutine. Utilisez plutôt asyncio.run():

    import asyncio
    
    async def hello():
        await asyncio.sleep(1)
        print('Hello')
    
    asyncio.run(hello())
    
  2. Objets attendus : Les coroutines, les futurs et les tâches sont les principaux objets attendus. Les coroutines Python sont attendues et peuvent être attendues d'autres coroutines.

  3. Mot clé d'attente :attendre ne peut être utilisé que dans les fonctions asynchrones.

    async def hello():
        await asyncio.sleep(1)
        print("Hello")
    
  4. Compatibilité : Tous les modules Python ne sont pas compatibles avec la programmation asynchrone. Par exemple, remplacer wait asyncio.sleep() par time.sleep() provoquera une erreur. Vous pouvez consulter la liste des modules compatibles et maintenus ici.

Dans la section suivante, nous explorerons une utilisation courante de la programmation asynchrone, les requêtes HTTP.

Exemple de programme : requêtes asynchrones

Jetons un coup d'œil au morceau de code suivant :

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())

Dans le code ci-dessus, nous créons deux fonctions asynchrones : une pour récupérer les données de l'URL de prévision-meteo et une fonction principale pour exécuter les processus dans le code Python. Le but est d'envoyer des requêtes HTTP GET asynchrones pour récupérer les températures et imprimer les réponses.

Dans les fonctions main et fetch, nous utilisons async with. Dans la fonction fetch, async with garantit que la connexion est correctement fermée. Dans la fonction principale, il garantit que la ClientSession est fermée une fois les demandes terminées. Ces pratiques sont importantes dans le codage asynchrone en Python pour gérer efficacement les ressources et éviter les fuites.

Dans la dernière ligne de la fonction principale, nous utilisons wait asyncio.gather(*tasks). Dans notre cas, il exécute toutes les tâches simultanément, permettant au programme d'envoyer plusieurs requêtes HTTP simultanément. L'utilisation de wait garantit que le programme attend que toutes les tâches soient terminées avant de continuer.

Sortir:

Temperature at marseille: 25 C
Temperature at toulouse: 24 C
Temperature at paris: 18 C
Program executed in 5.86 seconds.

Version synchrone pour comparaison

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.")

Sortir:

Temperature at Paris: 18 C
Temperature at Toulouse: 24 C
Temperature at Marseille: 25 C
Program executed in 9.01 seconds.

Quand utiliser la programmation asynchrone

Le modèle asynchrone fonctionne mieux lorsque :

  • Il existe un grand nombre de tâches, garantissant qu'au moins une tâche peut toujours progresser.

  • Les tâches impliquent des E/S importantes, ce qui fait qu'un programme asynchrone perd beaucoup de temps à se bloquer alors que d'autres tâches pourraient être en cours d'exécution.

  • Les tâches sont largement indépendantes, ce qui minimise la communication entre les tâches (et donc le fait qu'une tâche attende sur une autre).

Conclusion

Dans ce didacticiel, nous avons abordé :

  • Les concepts de programmation asynchrone et les concepts associés.

  • Utilisation efficace de async/await.

  • Faire des requêtes HTTP asynchrones avec aiohttp.

  • Les avantages de la programmation asynchrone.

Merci d'avoir lu. La deuxième partie couvrira la programmation asynchrone avec Django.

Ressources

  • Documentation Python : coroutines et tâches

  • Documentation Python : asyncio - E/S asynchrones

  • Documentation aiohttp

  • Bibliothèques Aio

  • Concurrence vs Parallélisme

Déclaration de sortie Cet article est reproduit sur : https://dev.to/koladev/asynchronous-programming-with-asyncio-3ad1?1 En cas de violation, veuillez contacter [email protected] pour le supprimer.
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3