Coroutines et tâches¶
Cette section donne un aperçu des API de haut-niveau du module asyncio pour utiliser les coroutines et les tâches.
Coroutines¶
Code source : Lib/asyncio/coroutines.py
Les coroutines déclarées avec la syntaxe async/await sont la manière privilégiée d’écrire des applications asynchrones. Par exemple, l’extrait de code suivant affiche « hello », attend une seconde et affiche ensuite « world » :
>>> import asyncio
>>> async def main():
... print('hello')
... await asyncio.sleep(1)
... print('world')
>>> asyncio.run(main())
hello
world
Remarquez que simplement appeler une coroutine ne la planifie pas pour exécution :
>>> main()
<coroutine object main at 0x1053bb7c8>
Pour réellement exécuter une coroutine, asyncio fournit les mécanismes suivants :
La fonction
asyncio.run()
pour exécuter la fonction « main() », le point d'entrée de haut-niveau (voir l'exemple ci-dessus).Attendre une coroutine. Le morceau de code suivant attend une seconde, affiche « hello », attend 2 secondes supplémentaires, puis affiche enfin « world » :
import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): print(f"started at {time.strftime('%X')}") await say_after(1, 'hello') await say_after(2, 'world') print(f"finished at {time.strftime('%X')}") asyncio.run(main())
Sortie attendue :
started at 17:13:52 hello world finished at 17:13:55
La fonction
asyncio.create_task()
pour exécuter de manière concurrente des coroutines en tant quetâches
asyncio.Modifions l'exemple ci-dessus et lançons deux coroutines
say_after
de manière concurrente :async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # Wait until both tasks are completed (should take # around 2 seconds.) await task1 await task2 print(f"finished at {time.strftime('%X')}")
La sortie attendue montre à présent que ce code s'exécute une seconde plus rapidement que le précédent :
started at 17:14:32 hello world finished at 17:14:34
La classe
asyncio.TaskGroup
fournit une alternative plus moderne àcreate_task()
. En utilisant cette API, le dernier exemple devient :async def main(): async with asyncio.TaskGroup() as tg: task1 = tg.create_task( say_after(1, 'hello')) task2 = tg.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # The await is implicit when the context manager exits. print(f"finished at {time.strftime('%X')}")
Le temps d'exécution et la sortie doivent être les mêmes que pour la version précédente.
Ajouté dans la version 3.11:
asyncio.TaskGroup
.
Attendables¶
Un objet est dit attendable (awaitable en anglais, c.-à-d. qui peut être attendu) s'il peut être utilisé dans une expression await
. Beaucoup d'API d'asyncio sont conçues pour accepter des attendables.
Il existe trois types principaux d'attendables : les coroutines, les tâches et les futurs.
Coroutines
Les coroutines sont des awaitables et peuvent donc être attendues par d'autres coroutines :
import asyncio
async def nested():
return 42
async def main():
# Nothing happens if we just call "nested()".
# A coroutine object is created but not awaited,
# so it *won't run at all*.
nested() # will raise a "RuntimeWarning".
# Let's do it differently now and await it:
print(await nested()) # will print "42".
asyncio.run(main())
Important
dans cette documentation, le terme « coroutine » est utilisé pour désigner deux concepts voisins :
une fonction coroutine : une fonction
async def
;un objet coroutine : un objet renvoyé par une fonction coroutine.
Tâches
Les tâches servent à planifier des coroutines de façon à ce qu'elles s'exécutent de manière concurrente.
Lorsqu'une coroutine est encapsulée dans une tâche à l'aide de fonctions comme asyncio.create_task()
, la coroutine est automatiquement planifiée pour s'exécuter prochainement :
import asyncio
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())
Futurs
Un Future
est un objet attendable spécial de bas-niveau, qui représente le résultat final d'une opération asynchrone.
Quand un objet Future est attendu, cela signifie que la coroutine attendra que ce futur soit résolu à un autre endroit.
Les objets Future d'asyncio sont nécessaires pour permettre l'exécution de code basé sur les fonctions de rappel avec la syntaxe async / await.
Il est normalement inutile de créer des objets Future dans la couche applicative du code.
Les objets Future, parfois exposés par des bibliothèques et quelques API d'asyncio, peuvent être attendus :
async def main():
await function_that_returns_a_future_object()
# this is also valid:
await asyncio.gather(
function_that_returns_a_future_object(),
some_python_coroutine()
)
loop.run_in_executor()
est l'exemple typique d'une fonction bas-niveau renvoyant un objet Future.
Création de tâches¶
Code source : Lib/asyncio/tasks.py
- asyncio.create_task(coro, *, name=None, context=None)¶
Encapsule la coroutine coro dans une tâche et la planifie pour exécution. Renvoie l'objet
Task
.Si name n’est pas
None
, il est défini comme le nom de la tâche en utilisantTask.set_name()
.L'argument (uniquement nommé) facultatif context permet de spécifier un
contextvars.Context
personnalisé pour la coroutine à exécuter. La copie de contexte actuelle est créée lorsqu'aucun context n'est fourni.La tâche est exécutée dans la boucle renvoyée par
get_running_loop()
;RuntimeError
est levée s'il n'y a pas de boucle en cours d'exécution dans le fil actuel.Note
asyncio.TaskGroup.create_task()
is a new alternative leveraging structural concurrency; it allows for waiting for a group of related tasks with strong safety guarantees.Important
gardez une référence au résultat de cette fonction, pour éviter qu'une tâche ne disparaisse en cours d'exécution. La boucle d'événements ne conserve que les références faibles aux tâches. Une tâche qui n'est pas référencée ailleurs peut être supprimée par le ramasse-miettes à tout moment, même avant qu'elle ne soit terminée. Pour créer des tâches d'arrière-plan fiables de type « lance et oublie », rassemblez-les dans une collection :
background_tasks = set() for i in range(10): task = asyncio.create_task(some_coro(param=i)) # Add task to the set. This creates a strong reference. background_tasks.add(task) # To prevent keeping references to finished tasks forever, # make each task remove its own reference from the set after # completion: task.add_done_callback(background_tasks.discard)
Ajouté dans la version 3.7.
Modifié dans la version 3.8: ajout du paramètre name.
Modifié dans la version 3.11: ajout du paramètre context.
Annulation de tâche¶
Les tâches peuvent être annulées facilement et en toute sécurité. Lorsqu'une tâche est annulée, asyncio.CancelledError
est levée dans la tâche à la première occasion.
Il est recommandé que les coroutines utilisent des blocs try/finally
pour exécuter de manière robuste la logique de nettoyage. Dans le cas où asyncio.CancelledError
est explicitement interceptée, elle devrait généralement être propagée lorsque le nettoyage est terminé. asyncio.CancelledError
sous-classe directement BaseException
donc la plupart du code n'a pas besoin d'en être conscient.
Les composants asyncio qui permettent la concurrence structurée, comme asyncio.TaskGroup
et asyncio.timeout()
, sont implémentés en utilisant l'annulation en interne et peuvent mal se comporter si une coroutine ne propage pas asyncio.CancelledError
. De même, le code utilisateur ne doit généralement pas appeler uncancel
. Cependant, dans les cas où la suppression de asyncio.CancelledError
est vraiment souhaitée, il est également nécessaire d'appeler uncancel()
pour supprimer complètement l'état d'annulation.
Groupes de tâches¶
Les groupes de tâches combinent une API de création de tâches avec un moyen pratique et fiable d'attendre la fin de toutes les tâches du groupe.
- class asyncio.TaskGroup¶
Gestionnaire de contexte asynchrone responsable d’un groupe de tâches. Des tâches peuvent être ajoutées au groupe en utilisant
create_task()
. Toutes les tâches sont attendues à la sortie du gestionnaire de contexte.Ajouté dans la version 3.11.
- create_task(coro, *, name=None, context=None)¶
Crée une tâche dans ce groupe de tâches. La signature correspond à celle de
asyncio.create_task()
.
Exemple :
async def main():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(some_coro(...))
task2 = tg.create_task(another_coro(...))
print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")
L'instruction async with
attend la fin de toutes les tâches du groupe. Lors de l'attente, de nouvelles tâches peuvent encore être ajoutées au groupe (par exemple, en passant tg
dans l'une des coroutines et en appelant tg.create_task()
dans cette coroutine). Une fois que la dernière tâche est terminée et que le bloc async with
est quitté, aucune nouvelle tâche ne peut être ajoutée au groupe.
La première fois que l'une des tâches appartenant au groupe échoue avec une exception autre que asyncio.CancelledError
, les tâches restantes du groupe sont annulées. Aucune autre tâche ne peut alors être ajoutée au groupe. À ce stade, si le corps de l'instruction async with
est toujours actif (par exemple, __aexit__()
n'a pas encore été appelé), la tâche contenant directement l'instruction async with
est également annulée. Le résultat asyncio.CancelledError
interrompt un await
, mais il ne sort pas de l'instruction async with
englobante.
Une fois toutes les tâches terminées, si des tâches ont échoué avec une exception autre que asyncio.CancelledError
, ces exceptions sont combinées dans un ExceptionGroup
ou BaseExceptionGroup
(selon le cas ; voir leur documentation) qui est ensuite levé.
Deux exceptions de base sont traitées spécialement : si une tâche échoue avec KeyboardInterrupt
ou SystemExit
, le groupe de tâches annule toujours les tâches restantes et les attend, mais alors la KeyboardInterrupt
ou la SystemExit
initiale est levée à nouveau au lieu de ExceptionGroup
ou BaseExceptionGroup
.
Si le corps de l'instruction async with
se termine avec une exception (donc __aexit__()
est appelé avec un ensemble d'exceptions), cela est traité de la même manière que si l'une des tâches échouait : les tâches restantes sont annulées puis attendues, et les exceptions de non-annulation sont regroupées dans un groupe d'exceptions et levées. L'exception transmise à __aexit__()
, à moins qu'il ne s'agisse de asyncio.CancelledError
, est également incluse dans le groupe d'exceptions. Le même cas spécial concerne KeyboardInterrupt
et SystemExit
comme dans le paragraphe précédent.
Terminating a Task Group¶
While terminating a task group is not natively supported by the standard library, termination can be achieved by adding an exception-raising task to the task group and ignoring the raised exception:
import asyncio
from asyncio import TaskGroup
class TerminateTaskGroup(Exception):
"""Exception raised to terminate a task group."""
async def force_terminate_task_group():
"""Used to force termination of a task group."""
raise TerminateTaskGroup()
async def job(task_id, sleep_time):
print(f'Task {task_id}: start')
await asyncio.sleep(sleep_time)
print(f'Task {task_id}: done')
async def main():
try:
async with TaskGroup() as group:
# spawn some tasks
group.create_task(job(1, 0.5))
group.create_task(job(2, 1.5))
# sleep for 1 second
await asyncio.sleep(1)
# add an exception-raising task to force the group to terminate
group.create_task(force_terminate_task_group())
except* TerminateTaskGroup:
pass
asyncio.run(main())
Expected output:
Task 1: start
Task 2: start
Task 1: done
Attente¶
- coroutine asyncio.sleep(delay, result=None)¶
Attend pendant delay secondes.
Si result est spécifié, il est renvoyé à l'appelant quand la coroutine se termine.
sleep()
suspend systématiquement la tâche courante, ce qui permet aux autres tâches de s'exécuter.Définir le délai sur 0 fournit un chemin optimisé pour permettre à d'autres tâches de s'exécuter. Cela peut être utilisé par les fonctions de longue durée pour éviter de bloquer la boucle d'événements pendant toute la durée de l'appel de fonction.
Exemple d'une coroutine affichant la date toutes les secondes pendant 5 secondes :
import asyncio import datetime async def display_date(): loop = asyncio.get_running_loop() end_time = loop.time() + 5.0 while True: print(datetime.datetime.now()) if (loop.time() + 1.0) >= end_time: break await asyncio.sleep(1) asyncio.run(display_date())
Modifié dans la version 3.10: le paramètre loop a été enlevé.
Exécution de tâches de manière concurrente¶
- awaitable asyncio.gather(*aws, return_exceptions=False)¶
Exécute les objets awaitable de la séquence aws, de manière concurrente.
Si un attendable de aws est une coroutine, celui-ci est automatiquement planifié comme une tâche Task.
Si tous les awaitables s'achèvent avec succès, le résultat est la liste des valeurs renvoyées. L'ordre de cette liste correspond à l'ordre des awaitables dans aws.
Si return_exceptions vaut
False
(valeur par défaut), la première exception levée est immédiatement propagée vers la tâche en attente dans legather()
. Les autres attendables dans la séquence aws ne sont pas annulés et poursuivent leur exécution.Si return_exceptions vaut
True
, les exceptions sont traitées de la même manière que les exécutions normales, et incluses dans la liste des résultats.Si
gather()
est annulé, tous les awaitables en cours (ceux qui n'ont pas encore fini de s'exécuter) sont également annulés.Si n'importe quel Task ou Future de la séquence aws est annulé, il est traité comme s'il avait levé
CancelledError
— l'appel àgather()
n'est alors pas annulé. Ceci permet d'empêcher que l'annulation d'une tâche ou d'un futur entraîne l'annulation des autres tâches ou futurs.Note
A new alternative to create and run tasks concurrently and wait for their completion is
asyncio.TaskGroup
. TaskGroup provides stronger safety guarantees than gather for scheduling a nesting of subtasks: if a task (or a subtask, a task scheduled by a task) raises an exception, TaskGroup will, while gather will not, cancel the remaining scheduled tasks).Exemple :
import asyncio async def factorial(name, number): f = 1 for i in range(2, number + 1): print(f"Task {name}: Compute factorial({number}), currently i={i}...") await asyncio.sleep(1) f *= i print(f"Task {name}: factorial({number}) = {f}") return f async def main(): # Schedule three calls *concurrently*: L = await asyncio.gather( factorial("A", 2), factorial("B", 3), factorial("C", 4), ) print(L) asyncio.run(main()) # Expected output: # # Task A: Compute factorial(2), currently i=2... # Task B: Compute factorial(3), currently i=2... # Task C: Compute factorial(4), currently i=2... # Task A: factorial(2) = 2 # Task B: Compute factorial(3), currently i=3... # Task C: Compute factorial(4), currently i=3... # Task B: factorial(3) = 6 # Task C: Compute factorial(4), currently i=4... # Task C: factorial(4) = 24 # [2, 6, 24]
Note
If return_exceptions is false, cancelling gather() after it has been marked done won't cancel any submitted awaitables. For instance, gather can be marked done after propagating an exception to the caller, therefore, calling
gather.cancel()
after catching an exception (raised by one of the awaitables) from gather won't cancel any other awaitables.Modifié dans la version 3.7: Si gather est lui-même annulé, l'annulation est propagée indépendamment de return_exceptions.
Modifié dans la version 3.10: le paramètre loop a été enlevé.
Obsolète depuis la version 3.10: Un avertissement d'obsolescence est émis si aucun argument positionnel n'est fourni ou si tous les arguments positionnels ne sont pas des objets de type Future et qu'il n'y a pas de boucle d'événements en cours d'exécution.
Eager Task Factory¶
- asyncio.eager_task_factory(loop, coro, *, name=None, context=None)¶
A task factory for eager task execution.
When using this factory (via
loop.set_task_factory(asyncio.eager_task_factory)
), coroutines begin execution synchronously duringTask
construction. Tasks are only scheduled on the event loop if they block. This can be a performance improvement as the overhead of loop scheduling is avoided for coroutines that complete synchronously.A common example where this is beneficial is coroutines which employ caching or memoization to avoid actual I/O when possible.
Note
Immediate execution of the coroutine is a semantic change. If the coroutine returns or raises, the task is never scheduled to the event loop. If the coroutine execution blocks, the task is scheduled to the event loop. This change may introduce behavior changes to existing applications. For example, the application's task execution order is likely to change.
Ajouté dans la version 3.12.
- asyncio.create_eager_task_factory(custom_task_constructor)¶
Create an eager task factory, similar to
eager_task_factory()
, using the provided custom_task_constructor when creating a new task instead of the defaultTask
.custom_task_constructor must be a callable with the signature matching the signature of
Task.__init__
. The callable must return aasyncio.Task
-compatible object.This function returns a callable intended to be used as a task factory of an event loop via
loop.set_task_factory(factory)
).Ajouté dans la version 3.12.
Protection contre l'annulation¶
- awaitable asyncio.shield(aw)¶
Empêche qu'un objet awaitable puisse être
annulé
.Si aw est une coroutine, elle est planifiée automatiquement comme une tâche.
L'instruction :
task = asyncio.create_task(something()) res = await shield(task)
est équivalente à :
res = await something()
à la différence près que, si la coroutine qui la contient est annulée, la tâche s'exécutant dans
something()
n'est pas annulée. Du point de vue desomething()
, il n'y a pas eu d'annulation. Cependant, son appelant est bien annulé, donc l'expression await lève bien uneCancelledError
.Si
something()
est annulée d'une autre façon (c.-à-d. depuis elle-même) ceci annule égalementshield()
.Pour ignorer complètement l'annulation (déconseillé), la fonction
shield()
peut être combinée à une clause try / except, comme dans le code ci-dessous :task = asyncio.create_task(something()) try: res = await shield(task) except CancelledError: res = None
Important
sauvegardez une référence aux tâches passées à cette fonction, pour éviter qu'une tâche ne disparaisse en cours d'exécution. La boucle d'événements ne conserve que les références faibles aux tâches. Une tâche qui n'est pas référencée ailleurs peut faire l'objet d'une suppression par le ramasse-miettes à tout moment, même avant qu'elle ne soit terminée.
Modifié dans la version 3.10: le paramètre loop a été enlevé.
Obsolète depuis la version 3.10: un avertissement d'obsolescence est émis si aw n'est pas un objet de type future et qu'il n'y a pas de boucle d'événement en cours d'exécution.
Délais d'attente¶
- asyncio.timeout(delay)¶
Return an asynchronous context manager that can be used to limit the amount of time spent waiting on something.
delay peut-être soit
None
, soit le nombre de secondes (entier ou décimal) d'attente. Si delay vautNone
, aucune limite n'est appliquée ; cela peut être utile lorsque le délai d'attente est inconnu au moment de la création du gestionnaire de contexte.Dans les deux cas, le gestionnaire de contexte peut être redéfini après sa création en utilisant
Timeout.reschedule()
.Exemple :
async def main(): async with asyncio.timeout(10): await long_running_task()
If
long_running_task
takes more than 10 seconds to complete, the context manager will cancel the current task and handle the resultingasyncio.CancelledError
internally, transforming it into aTimeoutError
which can be caught and handled.Note
The
asyncio.timeout()
context manager is what transforms theasyncio.CancelledError
into aTimeoutError
, which means theTimeoutError
can only be caught outside of the context manager.Example of catching
TimeoutError
:async def main(): try: async with asyncio.timeout(10): await long_running_task() except TimeoutError: print("The long operation timed out, but we've handled it.") print("This statement will run regardless.")
Le gestionnaire de contexte produit par
asyncio.timeout()
peut être reprogrammé à une échéance différente et inspecté.- class asyncio.Timeout(when)¶
Gestionnaire de contexte asynchrone pour annuler les coroutines en retard.
when
doit être un temps absolu au bout duquel le contexte doit expirer, tel que mesuré par l'horloge de la boucle d'événements :Si
when
vautNone
, le délai maximal ne se déclenche jamais.Si
when < loop.time()
, le délai maximal se déclenche à la prochaine itération de la boucle d'événement.
Exemple :
async def main(): try: # We do not know the timeout when starting, so we pass ``None``. async with asyncio.timeout(None) as cm: # We know the timeout now, so we reschedule it. new_deadline = get_running_loop().time() + 10 cm.reschedule(new_deadline) await long_running_task() except TimeoutError: pass if cm.expired(): print("Looks like we haven't finished on time.")
Les gestionnaires de contexte de délai maximal peuvent être imbriqués en toute sécurité.
Ajouté dans la version 3.11.
- asyncio.timeout_at(when)¶
Semblable à
asyncio.timeout()
, sauf que when est le temps absolu pour arrêter d'attendre, ouNone
.Exemple :
async def main(): loop = get_running_loop() deadline = loop.time() + 20 try: async with asyncio.timeout_at(deadline): await long_running_task() except TimeoutError: print("The long operation timed out, but we've handled it.") print("This statement will run regardless.")
Ajouté dans la version 3.11.
- coroutine asyncio.wait_for(aw, timeout)¶
Attend la fin de l'awaitable aw avec délai d'attente.
Si aw est une coroutine, elle est planifiée automatiquement comme une tâche.
timeout peut-être soit
None
, soit le nombre de secondes (entier ou décimal) d'attente. Si timeout vautNone
, la fonction s'interrompt jusqu'à ce que le futur s'achève.Si le délai d'attente maximal est dépassé, la tâche est annulée et l'exception
TimeoutError
est levée.Pour empêcher
l'annulation
de la tâche, il est nécessaire de l'encapsuler dans une fonctionshield()
.Cette fonction attend que le futur soit réellement annulé, donc le temps d'attente total peut être supérieur à timeout. Si une exception se produit lors de l'annulation, elle est propagée.
Si l'attente est annulée, le futur aw est également annulé.
Exemple :
async def eternity(): # Sleep for one hour await asyncio.sleep(3600) print('yay!') async def main(): # Wait for at most 1 second try: await asyncio.wait_for(eternity(), timeout=1.0) except TimeoutError: print('timeout!') asyncio.run(main()) # Expected output: # # timeout!
Modifié dans la version 3.7: Si le dépassement du délai d'attente maximal provoque l'annulation de aw,
wait_for
attend que aw soit annulée. Auparavant, l'exceptionTimeoutError
était immédiatement levée.Modifié dans la version 3.10: le paramètre loop a été enlevé.
Modifié dans la version 3.11: Raises
TimeoutError
instead ofasyncio.TimeoutError
.
Primitives d'attente¶
- coroutine asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)¶
Exécute les instances
Future
etTask
de l'itérable aws de manière concurrente, et s'interrompt jusqu'à ce que la condition décrite dans return_when soit vraie.The aws iterable must not be empty.
Renvoie deux ensembles de Tasks / Futures :
(done, pending)
.Utilisation :
done, pending = await asyncio.wait(aws)
timeout (entier ou décimal), si précisé, peut-être utilisé pour contrôler le nombre maximal de secondes d'attente avant de se terminer.
Cette fonction ne lève pas
TimeoutError
. Les futurs et les tâches qui ne sont pas finis quand le délai d'attente maximal est dépassé sont tout simplement renvoyés dans le second ensemble.return_when indique quand la fonction doit se terminer. Il peut prendre les valeurs suivantes :
Constante
Description
- asyncio.FIRST_COMPLETED¶
La fonction se termine lorsque n'importe quel futur se termine ou est annulé.
- asyncio.FIRST_EXCEPTION¶
The function will return when any future finishes by raising an exception. If no future raises an exception then it is equivalent to
ALL_COMPLETED
.- asyncio.ALL_COMPLETED¶
La fonction se termine lorsque les futurs sont tous finis ou annulés.
À la différence de
wait_for()
,wait()
n'annule pas les futurs quand le délai d'attente est dépassé.Modifié dans la version 3.10: le paramètre loop a été enlevé.
Modifié dans la version 3.11: Passer directement des objets coroutines à
wait()
est interdit.Modifié dans la version 3.12: Added support for generators yielding tasks.
- asyncio.as_completed(aws, *, timeout=None)¶
Run awaitable objects in the aws iterable concurrently. Return an iterator of coroutines. Each coroutine returned can be awaited to get the earliest next result from the iterable of the remaining awaitables.
Lève une exception
TimeoutError
si le délai d'attente est dépassé avant que tous les futurs ne soient achevés.Exemple :
for coro in as_completed(aws): earliest_result = await coro # ...
Modifié dans la version 3.10: le paramètre loop a été enlevé.
Obsolète depuis la version 3.10: Un avertissement d'obsolescence est émis si tous les objets en attente dans l'itérable aws ne sont pas des objets de type Future et qu'il n'y a pas de boucle d'événement en cours d'exécution.
Modifié dans la version 3.12: Added support for generators yielding tasks.
Exécution dans des fils d'exécution (threads)¶
- coroutine asyncio.to_thread(func, /, *args, **kwargs)¶
Exécute la fonction func de manière asynchrone dans un fil d'exécution séparé.
Tous les *args et **kwargs fournis à cette fonction sont directement passés à func. De plus, le
contextvars.Context
actuel est propagé, ce qui permet d'accéder aux variables de contexte du fil de boucle d'événements dans le fil séparé.Renvoie une coroutine qui peut être attendue pour obtenir le résultat éventuel de func.
Cette fonction coroutine est principalement destinée à être utilisée pour exécuter des fonctions/méthodes faisant beaucoup d'entrées-sorties et qui bloqueraient autrement la boucle d'événements si elles étaient exécutées dans le fil d'exécution principal. Par exemple :
def blocking_io(): print(f"start blocking_io at {time.strftime('%X')}") # Note that time.sleep() can be replaced with any blocking # IO-bound operation, such as file operations. time.sleep(1) print(f"blocking_io complete at {time.strftime('%X')}") async def main(): print(f"started main at {time.strftime('%X')}") await asyncio.gather( asyncio.to_thread(blocking_io), asyncio.sleep(1)) print(f"finished main at {time.strftime('%X')}") asyncio.run(main()) # Expected output: # # started main at 19:50:53 # start blocking_io at 19:50:53 # blocking_io complete at 19:50:54 # finished main at 19:50:54
Appeler directement
blocking_io()
dans n'importe quelle coroutine bloquerait la boucle d'événements pendant sa durée, ce qui entraînerait une seconde supplémentaire de temps d'exécution. Au lieu de cela, en utilisantasyncio.to_thread()
, nous pouvons l'exécuter dans un fil d'exécution séparé sans bloquer la boucle d'événements.Note
en raison du GIL,
asyncio.to_thread()
ne peut généralement être utilisée que pour rendre les fonctions faisant beaucoup d'entrées-sorties non bloquantes. Cependant, pour les modules d'extension qui relâchent le GIL ou les implémentations Python alternatives qui n'en ont pas,asyncio.to_thread()
peut également être utilisée pour les fonctions qui sollicitent beaucoup le processeur.Ajouté dans la version 3.9.
Planification depuis d'autres fils d'exécution¶
- asyncio.run_coroutine_threadsafe(coro, loop)¶
Enregistre une coroutine dans la boucle d'exécution actuelle. Cette opération est compatible avec les programmes à multiples fils d'exécution (thread-safe).
Renvoie un
concurrent.futures.Future
pour attendre le résultat d'un autre fil d'exécution du système d'exploitation.Cette fonction est faite pour être appelée par un fil d'exécution distinct de celui dans laquelle la boucle d'événement s'exécute. Exemple :
# Create a coroutine coro = asyncio.sleep(1, result=3) # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(coro, loop) # Wait for the result with an optional timeout argument assert future.result(timeout) == 3
Si une exception est levée dans une coroutine, le futur renvoyé en sera averti. Elle peut également être utilisée pour annuler la tâche de la boucle d'événement :
try: result = future.result(timeout) except TimeoutError: print('The coroutine took too long, cancelling the task...') future.cancel() except Exception as exc: print(f'The coroutine raised an exception: {exc!r}') else: print(f'The coroutine returned: {result!r}')
Voir la section exécution concurrente et multi-fils d'exécution de la documentation.
À la différence des autres fonctions d'asyncio, cette fonction requiert que loop soit passé de manière explicite.
Ajouté dans la version 3.5.1.
Introspection¶
- asyncio.current_task(loop=None)¶
Renvoie l'instance de la
Task
en cours d'exécution, ouNone
s'il n'y a pas de tâche en cours.Si loop vaut
None
,get_running_loop()
est appelée pour récupérer la boucle en cours d'exécution.Ajouté dans la version 3.7.
- asyncio.all_tasks(loop=None)¶
Renvoie l'ensemble des
Task
non terminés en cours d'exécution dans la boucle.Si loop vaut
None
,get_running_loop()
est appelée pour récupérer la boucle en cours d'exécution.Ajouté dans la version 3.7.
- asyncio.iscoroutine(obj)¶
Renvoie
True
si obj est un objet coroutine.Ajouté dans la version 3.4.
Objets Task¶
- class asyncio.Task(coro, *, loop=None, name=None, context=None, eager_start=False)¶
Objet compatible avec
Future
qui exécute une coroutine Python. Cet objet n'est pas utilisable dans des programmes à fils d'exécution multiples.Les tâches servent à exécuter des coroutines dans des boucles d'événements. Si une coroutine attend un futur, la tâche interrompt son exécution et attend la fin de ce futur. Quand celui-ci est terminé, l'exécution de la coroutine encapsulée reprend.
Les boucles d'événement fonctionnent de manière coopérative : une boucle d'événement exécute une tâche à la fois. Quand une tâche attend la fin d'un futur, la boucle d'événement exécute d'autres tâches, des fonctions de rappel, ou effectue des opérations d'entrées-sorties.
La fonction de haut niveau
asyncio.create_task()
et les fonctions de bas-niveauloop.create_task()
ouensure_future()
permettent de créer des tâches. Il est déconseillé d'instancier manuellement des objets Task.La méthode
cancel()
d'une tâche en cours d'exécution permet d'annuler celle-ci. L'appel de cette méthode force la tâche à lever l'exceptionCancelledError
dans la coroutine encapsulée. Si la coroutine attendait un futur au moment de l'annulation, celui-ci est annulé.La méthode
cancelled()
permet de vérifier si la tâche a été annulée. Elle renvoieTrue
si la coroutine encapsulée n'a pas ignoré l'exceptionCancelledError
et a bien été annulée.asyncio.Task
hérite deFuture
, de toute son API, à l'exception deFuture.set_result()
et deFuture.set_exception()
.An optional keyword-only context argument allows specifying a custom
contextvars.Context
for the coro to run in. If no context is provided, the Task copies the current context and later runs its coroutine in the copied context.An optional keyword-only eager_start argument allows eagerly starting the execution of the
asyncio.Task
at task creation time. If set toTrue
and the event loop is running, the task will start executing the coroutine immediately, until the first time the coroutine blocks. If the coroutine returns or raises without blocking, the task will be finished eagerly and will skip scheduling to the event loop.Modifié dans la version 3.7: ajout du support du module
contextvars
.Modifié dans la version 3.8: ajout du paramètre name.
Obsolète depuis la version 3.10: un avertissement d'obsolescence est émis si loop n'est pas spécifié et qu'il n'y a pas de boucle d'événement en cours d'exécution.
Modifié dans la version 3.11: ajout du paramètre context.
Modifié dans la version 3.12: Added the eager_start parameter.
- done()¶
Renvoie
True
si la tâche est achevée.Une tâche est dite achevée quand la coroutine encapsulée a soit renvoyé une valeur, soit levé une exception, ou que la tâche a été annulée.
- result()¶
Renvoie le résultat de la tâche.
Si la tâche est achevée, le résultat de la coroutine encapsulée est renvoyé (sinon, dans le cas où la coroutine a levé une exception, cette exception est de nouveau levée).
Si la tâche a été annulée, cette méthode lève une exception
CancelledError
.If the Task's result isn't yet available, this method raises an
InvalidStateError
exception.
- exception()¶
Renvoie l'exception de la tâche.
Si la coroutine encapsulée lève une exception, cette exception est renvoyée. Si la coroutine s'est exécutée normalement, cette méthode renvoie
None
.Si la tâche a été annulée, cette méthode lève une exception
CancelledError
.Si la tâche n'est pas encore achevée, cette méthode lève une exception
InvalidStateError
.
- add_done_callback(callback, *, context=None)¶
Ajoute une fonction de rappel qui sera exécutée quand la tâche sera achevée.
Cette méthode ne doit être utilisée que dans du code basé sur les fonctions de rappel de bas-niveau.
Se référer à la documentation de
Future.add_done_callback()
pour plus de détails.
- remove_done_callback(callback)¶
Retire callback de la liste de fonctions de rappel.
Cette méthode ne doit être utilisée que dans du code basé sur les fonctions de rappel de bas-niveau.
Se référer à la documentation de
Future.remove_done_callback()
pour plus de détails.
- get_stack(*, limit=None)¶
Renvoie une liste représentant la pile d'appels de la tâche.
Si la coroutine encapsulée n'est pas terminée, cette fonction renvoie la pile d'appels à partir de l'endroit où celle-ci est interrompue. Si la coroutine s'est terminée normalement ou a été annulée, cette fonction renvoie une liste vide. Si la coroutine a été terminée par une exception, ceci renvoie la pile d'erreurs.
La pile est toujours affichée de l'appelant à l'appelé.
Une seule ligne est renvoyée si la coroutine est suspendue.
L'argument facultatif limit définit le nombre maximal d'appels à renvoyer ; par défaut, tous sont renvoyés. L'ordre de la liste diffère selon la nature de celle-ci : les appels les plus récents d'une pile d'appels sont renvoyés, si la pile est une pile d'erreurs, ce sont les appels les plus anciens qui le sont (dans un souci de cohérence avec le module traceback).
- print_stack(*, limit=None, file=None)¶
Affiche la pile d'appels ou d'erreurs de la tâche.
Le format de sortie des appels produits par
get_stack()
est similaire à celui du module traceback.Le paramètre limit est directement passé à
get_stack()
.Le paramètre file est un flux d'entrées-sorties sur lequel le résultat est écrit ; par défaut,
sys.stdout
.
- get_coro()¶
Renvoie l’objet coroutine encapsulé par la
Task
.Note
This will return
None
for Tasks which have already completed eagerly. See the Eager Task Factory.Ajouté dans la version 3.8.
Modifié dans la version 3.12: Newly added eager task execution means result may be
None
.
- get_context()¶
Return the
contextvars.Context
object associated with the task.Ajouté dans la version 3.12.
- get_name()¶
Renvoie le nom de la tâche.
Si aucun nom n’a été explicitement assigné à la tâche, l’implémentation par défaut d’une Task asyncio génère un nom par défaut durant l’instanciation.
Ajouté dans la version 3.8.
- set_name(value)¶
Définit le nom de la tâche.
L’argument value peut être n’importe quel objet qui sera ensuite converti en chaine de caractères.
Dans l’implémentation par défaut de Task, le nom sera visible dans le résultat de
repr()
d’un objet Task.Ajouté dans la version 3.8.
- cancel(msg=None)¶
Demande l'annulation d'une tâche.
Provisionne la levée de l'exception
CancelledError
dans la coroutine encapsulée. L'exception sera levée au prochain cycle de la boucle d'exécution.La coroutine peut alors faire le ménage ou même ignorer la requête en supprimant l'exception à l'aide d'un bloc
try
… …except CancelledError
…finally
. Par conséquent, contrairement àFuture.cancel()
,Task.cancel()
ne garantit pas que la tâche sera annulée, bien qu'ignorer totalement une annulation ne soit ni une pratique courante, ni encouragé. Si la coroutine décide néanmoins de supprimer l'annulation, elle doit appelerTask.uncancel()
en plus d'intercepter l'exception.Modifié dans la version 3.9: ajout du paramètre msg.
Modifié dans la version 3.11: le paramètre
msg
est propagé de la tâche annulée vers celle qui l'attend.L'exemple ci-dessous illustre comment une coroutine peut intercepter une requête d'annulation :
async def cancel_me(): print('cancel_me(): before sleep') try: # Wait for 1 hour await asyncio.sleep(3600) except asyncio.CancelledError: print('cancel_me(): cancel sleep') raise finally: print('cancel_me(): after sleep') async def main(): # Create a "cancel_me" Task task = asyncio.create_task(cancel_me()) # Wait for 1 second await asyncio.sleep(1) task.cancel() try: await task except asyncio.CancelledError: print("main(): cancel_me is cancelled now") asyncio.run(main()) # Expected output: # # cancel_me(): before sleep # cancel_me(): cancel sleep # cancel_me(): after sleep # main(): cancel_me is cancelled now
- cancelled()¶
Renvoie
True
si la tâche est annulée.La tâche est annulée quand l'annulation a été demandée avec
cancel()
et la coroutine encapsulée a propagé l'exceptionCancelledError
qui a été levée en son sein.
- uncancel()¶
Décrémente le nombre de demandes d'annulation pour cette tâche.
Renvoie le nombre restant de demandes d'annulation.
Notez qu'une fois l'exécution d'une tâche annulée terminée, les appels ultérieurs à
uncancel()
ne font rien.Ajouté dans la version 3.11.
Cette méthode est utilisée par les composants internes d'asyncio et elle ne devrait pas être utilisée par le code de l'utilisateur final. En particulier, si une tâche est annulée avec succès, cela permet aux structures permettant le multi-fils tels que Groupes de tâches et
asyncio.timeout()
de continuer à s'exécuter, isolant l'annulation au bloc concerné. Par exemple :async def make_request_with_timeout(): try: async with asyncio.timeout(1): # Structured block affected by the timeout: await make_request() await make_another_request() except TimeoutError: log("There was a timeout") # Outer code not affected by the timeout: await unrelated_code()
Alors que le bloc avec
make_request()
etmake_another_request()
peut être annulé en raison du délai d'attente,unrelated_code()
devrait continuer à s'exécuter même en cas d'atteinte du délai maximal. Ceci est implémenté avecuncancel()
. Les gestionnaires de contexteTaskGroup
utilisentuncancel()
de la même manière.If end-user code is, for some reason, suppressing cancellation by catching
CancelledError
, it needs to call this method to remove the cancellation state.
- cancelling()¶
Renvoie le nombre de demandes d'annulation en attente à cette tâche, c'est-à-dire le nombre d'appels à
cancel()
moins le nombre d'appels àuncancel()
.Notez que si ce nombre est supérieur à zéro mais que la tâche est toujours en cours d'exécution,
cancelled()
renvoie toujoursFalse
. En effet, ce nombre peut être réduit en appelantuncancel()
, ce qui peut empêcher en fin de compte la tâche d'être annulée si les demandes d'annulation tombent à zéro.Cette méthode est utilisée par les composants internes d'asyncio et ne devrait pas être utilisée par le code de l'utilisateur final. Voir
uncancel()
pour plus de détails.Ajouté dans la version 3.11.