Un programme informatique réalisé avec un mode de programmation synchrone consiste en une liste de différentes tâches qui s'exécutent les unes après les autres. Dans cette liste, une tâche doit attendre que les toutes les tâches précédents soient terminées avant d'être exécutée. De même, toutes les tâches successives doivent attendre que cette tâche soit terminée pour être à leur tour exécutées.
Ces tâches sont concurrentes et sont considérées comme bloquantes pour toutes les tâches qui suivent.
Cela convient pour de nombreux programmes informatiques.
La programmation asynchrone permet à un programme de lancer plusieurs tâches sans attendre que chacune de ces tâches soient terminées avant de passer à la suivante. Cette manière de programmer permet :
Pour cela il est nécessaire que les tâches le permettent, c'est à dire que certaines tâches soient indépendantes de la fin de l'exécution d'autres tâches.
Le modèle de programmation asynchrone permet alors de lancer plusieurs tâches :
Le langage Python dispose depuis la version 3.6 de la bibliothèque asyncio pour gérer des traitements asynchrones.
Installer la version python3.6 :
$ sudo nano /etc/apt/sources.list # add deb http://ftp.de.debian.org/debian testing main
$ sudo apt-get update
$ sudo apt-get -t testing install python3.6
liens :
Le composant principal de tout programme Python basé sur la bibliothèque asyncio est la gestion d'une boucle d'événement (event loop) qui se charge de lancer les différentes tâches (coroutines) du programme.
La boucle d'événements attend que des événements se produisent et fait correspondre à chacun de ces événements une fonction qui a été explicitement associée à ce type d'événement.
loop = asyncio.get_event_loop()
loop.run_until_complete(Coroutine()) loop.close()
# définir quelle **coroutine** doit être **exécutée** : asyncio.ensure_future(Coroutine()) loop.run_forever() loop.close()
Attention : cette méthode va faire boucler indéfiniment la boucle d'événement. Il faut alors gérer l'arrêt de la boucle
import asyncio # Définir la coroutine qui sera exécutée ultérieurement (future) async def Coroutine(): print("Exécution de la coroutine") # Création de la boucle d'événement (event loop) loop = asyncio.get_event_loop() # exécuter la boucle d'événement jusqu'à la fin de l'exécution de la coroutine loop.run_until_complete(Coroutine()) # fermer la boucle d'événement loop.close()
import asyncio # Définir la coroutine qui sera exécuter ultérieurement (future) async def Coroutine(): print("Exécution de la coroutine") # indiquer que la coroutine sera exécutée dans la boucle d'événement # Celle-ci est planifiée en arrière plan mais pour l'instant # le programme n'attend pas le résultat de son exécution asyncio.ensure_future(Coroutine()) # Création de la boucle d'événement (event loop) loop = asyncio.get_event_loop() #lancer indefiniment la boucle -> bouclage infini du programme loop.run_forever() # fermer la boucle d'événement -> ne sera jamais exécuté en l'état loop.close()
Pour arrêter la boucle d'événement il faut appeler la méthode stop(), par exemple dans la coroutine :
# Définir la coroutine qui sera exécuter ultérieurement (future) async def Coroutine(): print("Exécution de la coroutine") loop.stop()
Si la méthode stop() n'est appelée le programme va boucler indéfiniment.
Pour lancer plusieurs coroutines :
Le mot clé await sur une instruction de la coroutine qui est bloquante :
import time import asyncio # Définir deux coroutines qui seront exécutée ultérieurement (future) async def Coroutine_1(): print("Exécution de la coroutine 1") await asyncio.sleep(2) print("Fin de l'exécution de la coroutine 1") async def Coroutine_2(): print("Exécution de la coroutine 2") await asyncio.sleep(2) print("Fin de l'exécution de la coroutine 2") loop.stop() # Création de la boucle d'événement (event loop) loop = asyncio.get_event_loop() # indiquer les coroutines à exécuter dans la boucle d'événement asyncio.ensure_future(Coroutine_1()) asyncio.ensure_future(Coroutine_2()) #lancer indefiniment la boucle -> bouclage infini du programme loop.run_forever() # fermer la boucle d'événement loop.close()
import time import asyncio # Définir deux coroutines qui seront exécutée ultérieurement (future) async def Coroutine_1(): print("Exécution de la coroutine 1") await asyncio.sleep(2) print("Fin de l'exécution de la coroutine 1") async def Coroutine_2(): print("Exécution de la coroutine 2") await asyncio.sleep(2) print("Fin de l'exécution de la coroutine 2") loop.stop() # Création de la boucle d'événement (event loop) loop = asyncio.get_event_loop() # indiquer dans une variable liste_coroutines la liste de coroutines # à ajouter à la boucle d'événement liste_coroutines = asyncio.gather(Coroutine_1(), Coroutine_2()) #exécuter la boucle d'événement jusqu'à la fin de l'exécution des coroutines loop.run_until_complete(liste_coroutines) # fermer la boucle d'événement loop.close()
Dans les situations précédentes :
Voici comment on peut gérer l'appel de la coroutine 2 depuis la première :
import time import asyncio # Définir deux coroutines qui seront exécutée ultérieurement (future) async def Coroutine_1(): print("Exécution de la coroutine 1") await Coroutine_2() print("Fin de l'exécution de la coroutine 1") return "1" async def Coroutine_2(): print("Exécution de la coroutine 2") await asyncio.sleep(2) print("Fin de l'exécution de la coroutine 2") return "2" #loop.stop() # Création de la boucle d'événement (event loop) loop = asyncio.get_event_loop() print(loop.run_until_complete(Coroutine_1())) # fermer la boucle d'événement -> est maintenant exécuté loop.close()
Ressources :
Les tâches dans Asyncio sont responsables de l'exécution des coroutines dans une boucle d'événement. Ces tâches ne peuvent s'exécuter que dans une boucle d'événement à la fois et, pour réaliser l'exécution en parallèle, vous devez exécuter plusieurs boucles d'événements.
La fonction wait () permet d'attendre jusqu'à ce que les instances Future, les tâches, soient terminée. Cette fonction renvoie alors un ensemble de 2 ensembles nommés. Le premier jeu contient les tâches terminées, le second les tâches non terminées.
#envoyer des messages en parallèle envoyer = asyncio.ensure_future(gestion_envoi_message(websocket)) #recevoir message en parallèle recevoir = asyncio.ensure_future(gestion_reception_message(websocket)) termine, attente = await asyncio.wait( [envoyer, recevoir], return_when = asyncio.FIRST_COMPLETED, )