Maison >développement back-end >Tutoriel Python >Python haute performance : Asyncio
La programmation simultanée est une approche de programmation qui traite de l'exécution simultanée de plusieurs tâches. En Python, asyncio est un outil puissant pour implémenter la programmation asynchrone. Basé sur le concept de coroutines, asyncio peut gérer efficacement les tâches gourmandes en E/S. Cet article présentera les principes de base et l'utilisation d'asyncio.
Nous savons que lors de la gestion des opérations d'E/S, l'utilisation du multithreading peut considérablement améliorer l'efficacité par rapport à un seul thread normal. Alors, pourquoi avons-nous encore besoin d'asyncio ?
Le multithreading présente de nombreux avantages et est largement utilisé, mais il présente également certaines limites :
C'est précisément pour résoudre ces problèmes qu'asyncio a émergé.
Distinguons d'abord les concepts de Sync (synchrone) et d'Async (asynchrone).
En résumé, le principe de fonctionnement d'asyncio repose sur les mécanismes de coroutines et de boucles d'événements. En utilisant des coroutines pour les opérations asynchrones et en confiant la boucle d'événements responsable de la planification et de l'exécution des coroutines, asyncio réalise un modèle de programmation asynchrone efficace.
Les coroutines sont un concept important en asyncio. Ce sont des unités d'exécution légères qui peuvent basculer rapidement entre les tâches sans la surcharge de changement de thread. Les coroutines peuvent être définies avec le mot-clé async, et le mot-clé wait est utilisé pour suspendre l'exécution de la coroutine et reprendre une fois une certaine opération terminée.
Voici un exemple de code simple montrant comment utiliser les coroutines pour la programmation asynchrone :
import asyncio async def hello(): print("Hello") await asyncio.sleep(1) # Simulate a time-consuming operation print("World") # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(hello())
Dans cet exemple, la fonction hello() est une coroutine définie avec le mot-clé async. À l’intérieur de la coroutine, nous pouvons utiliser wait pour suspendre son exécution. Ici, asyncio.sleep(1) est utilisé pour simuler une opération fastidieuse. La méthode run_until_complete() ajoute la coroutine à la boucle d'événements et l'exécute.
asyncio est principalement utilisé pour gérer les tâches gourmandes en E/S, telles que les requêtes réseau, la lecture et l'écriture de fichiers. Il fournit une série d'API pour les opérations d'E/S asynchrones, qui peuvent être utilisées en combinaison avec le mot-clé wait pour réaliser facilement une programmation asynchrone.
Voici un exemple de code simple montrant comment utiliser asyncio pour les requêtes réseau asynchrones :
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'https://www.example.com') print(html) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
Dans cet exemple, nous utilisons la bibliothèque aiohttp pour les requêtes réseau. La fonction fetch() est une coroutine. Il lance une requête GET asynchrone via la méthode session.get() et attend le retour de la réponse à l'aide du mot-clé wait. La fonction main() est une autre coroutine. Il crée un objet ClientSession à l'intérieur pour le réutiliser, puis appelle la méthode fetch() pour obtenir le contenu de la page Web et l'imprimer.
Remarque : Ici, nous utilisons aiohttp au lieu de la bibliothèque de requêtes car la bibliothèque de requêtes n'est pas compatible avec asyncio, alors que la bibliothèque aiohttp l'est. Pour faire bon usage d'asyncio, notamment pour exercer ses puissantes fonctions, dans de nombreux cas, des bibliothèques Python correspondantes sont nécessaires.
asyncio fournit également certains mécanismes pour exécuter simultanément plusieurs tâches, tels que asyncio.gather() et asyncio.wait(). Voici un exemple de code montrant comment utiliser ces mécanismes pour exécuter simultanément plusieurs tâches de coroutine :
import asyncio async def task1(): print("Task 1 started") await asyncio.sleep(1) print("Task 1 finished") async def task2(): print("Task 2 started") await asyncio.sleep(2) print("Task 2 finished") async def main(): await asyncio.gather(task1(), task2()) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
Dans cet exemple, nous définissons deux tâches coroutines task1() et task2(), qui effectuent toutes deux des opérations fastidieuses. La coroutine main() démarre ces deux tâches simultanément via asyncio.gather() et attend qu'elles se terminent. L'exécution simultanée peut améliorer l'efficacité de l'exécution du programme.
Dans les projets réels, faut-il choisir le multithreading ou l'asyncio ? Un gros plan l'a résumé de manière vivante :
import asyncio async def hello(): print("Hello") await asyncio.sleep(1) # Simulate a time-consuming operation print("World") # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(hello())
Saisissez une liste. Pour chaque élément de la liste, nous voulons calculer la somme des carrés de tous les entiers de 0 à cet élément.
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'https://www.example.com') print(html) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
Le temps d'exécution est Le calcul prend 16,00943413000002 secondes
import asyncio async def task1(): print("Task 1 started") await asyncio.sleep(1) print("Task 1 finished") async def task2(): print("Task 2 started") await asyncio.sleep(2) print("Task 2 finished") async def main(): await asyncio.gather(task1(), task2()) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
Le temps d'exécution est Le calcul prend 7,314132894999999 secondes
Dans ce code amélioré, nous utilisons concurrent.futures.ProcessPoolExecutor pour créer un pool de processus, puis utilisons la méthode executor.map() pour soumettre des tâches et obtenir des résultats. Notez qu'après avoir utilisé executor.map(), si vous avez besoin d'obtenir les résultats, vous pouvez parcourir les résultats dans une liste ou utiliser d'autres méthodes pour traiter les résultats.
if io_bound: if io_slow: print('Use Asyncio') else: print('Use multi-threading') elif cpu_bound: print('Use multi-processing')
Le temps d'exécution est Le calcul prend 5,024221667 secondes
concurrent.futures.ProcessPoolExecutor et multiprocessing sont tous deux des bibliothèques permettant d'implémenter la concurrence multi-processus en Python. Il y a quelques différences :
En résumé, concurrent.futures.ProcessPoolExecutor est une interface de haut niveau qui encapsule les fonctions multi-processus sous-jacentes, adaptée à une parallélisation simple de tâches multi-processus. multiprocessing est une bibliothèque de plus bas niveau, offrant plus de contrôle et de flexibilité, adaptée aux scénarios nécessitant un contrôle plus fin des processus. Vous devez choisir la bibliothèque appropriée en fonction d'exigences spécifiques. S'il s'agit simplement d'une simple parallélisation de tâches, vous pouvez utiliser concurrent.futures.ProcessPoolExecutor pour simplifier le code ; si davantage de contrôle et de communication de bas niveau sont nécessaires, vous pouvez utiliser la bibliothèque multitraitement.
Contrairement au multithreading, asyncio est monothread, mais le mécanisme de sa boucle d'événements interne lui permet d'exécuter plusieurs tâches différentes simultanément et a un plus grand contrôle autonome que le multithreading.
Les tâches en asyncio ne seront pas interrompues pendant le fonctionnement, donc la situation de condition de concurrence ne se produira pas.
Particulièrement dans les scénarios avec des opérations d'E/S lourdes, asyncio a une efficacité opérationnelle plus élevée que le multithreading. Parce que le coût du changement de tâche en asyncio est bien inférieur à celui du changement de thread, et le nombre de tâches qu'asyncio peut démarrer est bien plus grand que le nombre de threads en multithreading.
Cependant, il convient de noter que dans de nombreux cas, l'utilisation d'asyncio nécessite le support de bibliothèques tierces spécifiques, comme aiohttp dans l'exemple précédent. Et si les opérations d'E/S sont rapides et peu lourdes, l'utilisation du multithreading peut également résoudre efficacement le problème.
Enfin, permettez-moi de vous présenter la plateforme idéale pour déployer Flask/FastAPI : Leapcell.
Leapcell est une plateforme de cloud computing spécialement conçue pour les applications distribuées modernes. Son modèle de tarification à l'utilisation garantit l'absence de coûts inutiles, ce qui signifie que les utilisateurs ne paient que pour les ressources qu'ils utilisent réellement.
Apprenez-en plus dans la documentation !
Twitter de Leapcell : https://x.com/LeapcellHQ
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!