8.7. Événements¶
asyncio.Event est la primitive de signalisation la plus simple que le module fournit. Une coroutine positionne (set) un événement lorsque quelque chose s’est produit ; un nombre quelconque d’autres coroutines attendent que l’événement soit positionné avant de continuer. Il n’y a pas de charge utile – un événement est un booléen qui passe à vrai et le reste jusqu’à ce que quelque chose le remette à zéro.
8.7.1. La forme de base¶
import asyncio
async def waiter(evt):
print("waiting")
await evt.wait()
print("got signal")
async def main():
evt = asyncio.Event()
asyncio.create_task(waiter(evt))
await asyncio.sleep(1)
evt.set()
await asyncio.sleep(0)
asyncio.run(main())
La tâche en attente s’exécute, atteint await evt.wait() et est suspendue – l’événement démarre à l’état effacé, donc l’appel wait rend la main à la boucle. Une seconde plus tard, main appelle evt.set() et la tâche en attente est planifiée pour reprendre. Le await asyncio.sleep(0) final n’est qu’un yield, afin que la boucle ait une chance d’exécuter la tâche en attente avant que main ne retourne.
8.7.2. Les quatre méthodes¶
set()– positionne l’événement et planifie la reprise de chaque coroutine actuellement bloquée surwait(). L’événement reste positionné jusqu’à ce qu’il soit effacé.clear()– remet l’événement à l’état effacé. Les coroutines qui fontawait wait()ensuite se bloqueront de nouveau.wait()– une coroutine. Si l’événement est déjà positionné, retourne immédiatement. Sinon, bloque jusqu’à ce que quelque chose le positionne.is_set()– renvoie l’état actuel sans bloquer. Utile lorsqu’une coroutine souhaite vérifier l’événement sans l’attendre.
8.7.3. Plusieurs tâches en attente¶
Plusieurs coroutines peuvent attendre (await) le même événement. Quand quelque chose appelle set(), elles sont toutes planifiées pour reprendre. L’événement est de type un-vers-plusieurs par défaut ; il n’est pas nécessaire d’avoir plusieurs événements pour diffuser vers plusieurs tâches en attente.
8.7.4. Réutiliser un événement¶
Un schéma courant consiste à utiliser un événement de manière répétée – une coroutine qui s’exécute une fois par signal – en l’effaçant après chaque passage:
async def worker(go):
while True:
await go.wait()
do_one_unit_of_work()
go.clear()
Le producteur positionne l’événement chaque fois qu’il y a du travail ; le travailleur l’efface une fois qu’il a récupéré le signal. Si le producteur risque de positionner à nouveau l’événement avant que le travailleur ne se soit réveillé, ce dernier peut souhaiter traiter ce cas (while go.is_set(): ...) avant de l’effacer.
8.7.5. Contextes sûrs¶
Il n’est pas sûr d’appeler set() sur un Event depuis l’intérieur d’un gestionnaire d’interruption. Le mécanisme qu’il utilise pour planifier les tâches en attente suppose qu’il s’exécute dans le contexte même de la boucle d’événements, ce qui n’est pas le cas des interruptions. Pour réveiller une tâche asyncio depuis un gestionnaire d’interruption, ThreadSafeFlag (traité sous peu) est la bonne primitive.