Comment créer et exécuter des tâches simultanées à l'aide de python module asyncio?
Je suis en train d'essayer de bien comprendre et de mettre en œuvre deux simultanément en cours d'exécution Tâche
objets à l'aide de Python 3 est relativement nouveau asyncio
module.
En un mot, asyncio semble avoir été conçu pour gérer le processus asynchrones et simultanées Task
exécution sur une boucle d'événements. Il encourage l'utilisation de await
(appliquée dans async fonctions) comme un rappel sans attendre et utiliser un résultat, sans blocage de la boucle d'événements. (Contrats à terme et les rappels sont toujours une alternative viable.)
Il fournit également le asyncio.Task()
classe, spécialisé de la sous-classe de Future
conçu pour envelopper coroutines. De préférence, invoquée par l'aide de la asyncio.ensure_future()
méthode. L'utilisation prévue de asyncio tâches est de permettre indépendamment l'exécution de tâches à exécuter simultanément avec les autres tâches au sein de la même boucle d'événements. Ma compréhension est que Tasks
sont connectés à la boucle d'événements qui automatiquement de maintenir la coroutine entre await
consolidés.
J'aime l'idée d'être en mesure de l'utilisation simultanée de Tâches, sans avoir besoin d'utiliser l'un des Exécuteur
classes, mais je n'ai pas trouvé trop de détails sur la mise en œuvre.
C'est de cette façon que je suis en train de le faire:
import asyncio
print('running async test')
async def say_boo():
i = 0
while True:
await asyncio.sleep(0)
print('...boo {0}'.format(i))
i += 1
async def say_baa():
i = 0
while True:
await asyncio.sleep(0)
print('...baa {0}'.format(i))
i += 1
# wrap in Task object
# -> automatically attaches to event loop and executes
boo = asyncio.ensure_future(say_boo())
baa = asyncio.ensure_future(say_baa())
loop = asyncio.get_event_loop()
loop.run_forever()
Dans le cas d'essayer d'exécuter simultanément deux boucle Tâches, j'ai remarqué que, à moins que la Tâche a un interne await
expression, il sera coincé dans le while
boucle, bloquant d'autres tâches de s'exécuter (un peu comme une normale while
boucle). Cependant, dès Tâches (a)attendre, ils semblent fonctionner simultanément sans problème.
Ainsi, la await
déclarations semblent offrir la boucle d'événement avec un point d'appui pour des allers-retours entre les tâches, ce qui donne l'effet de simultanéité.
Exemple de sortie interne await
:
running async test
...boo 0
...baa 0
...boo 1
...baa 1
...boo 2
...baa 2
Exemple de sortie sans interne await
:
...boo 0
...boo 1
...boo 2
...boo 3
...boo 4
Questions
Est-ce à la mise en œuvre de passer pour un " bon " exemple de connexions simultanées en boucle Tâches dans asyncio
?
Est-il exact que la seule façon dont cela fonctionne est pour un Task
de fournir un point de blocage (await
expression) pour que la boucle d'événement de jongler avec plusieurs tâches?
- Oui, la tâche atomiquement exécute lui-même à partir de
yield from
à la prochaineyield from
.
Vous devez vous connecter pour publier un commentaire.
Oui, tout coroutine qui s'exécute dans votre boucle d'événement permettra de bloquer les autres coroutines et les tâches en cours d'exécution, à moins qu'il
yield from
ouawait
(si vous utilisez Python 3.5+).C'est parce que
asyncio
est mono-thread, le seul moyen pour que la boucle d'événements pour exécuter n'a pas d'autre coroutine à être activement en cours d'exécution. À l'aide deyield from
/await
suspend la coroutine temporairement, en donnant la boucle d'événement de la chance de travailler.Votre code d'exemple est très bien, mais dans de nombreux cas, vous ne souhaitez probablement pas long-exécution de code qui n'est pas de faire des e/S asynchrones fonctionnant à l'intérieur de la boucle d'événement pour commencer. Dans ces cas, il est souvent plus judicieux d'utiliser
BaseEventLoop.run_in_executor
pour exécuter le code dans un thread d'arrière-plan ou d'un processus.ProcessPoolExecutor
serait le meilleur choix si votre tâche est lié au PROCESSEUR,ThreadPoolExecutor
serait utilisé si vous avez besoin de faire quelques I/O qui n'est pasasyncio
de l'environnement.Vos deux boucles, par exemple, sont complètement lié au PROCESSEUR et ne partagez pas un état, de sorte que le meilleur rendement d'utiliser
ProcessPoolExecutor
pour exécuter chaque boucle en parallèle entre les Processeurs:asyncio.async
devrait être utilisée au lieu de laasyncio.Task
constructeur directement. Nous ne voulons passay_boo
etsay_baa
être coroutines, ils devraient l'être ordinaire des fonctions qui s'exécutent en dehors de la boucle d'événements, de sorte que vous ne devez pas ajouter d'yield from
les appels ou les emballer dans uneasyncio.Task
.ensure_future
maintenant queasync
est obsolète.Vous n'avez pas nécessairement besoin d'un
yield from x
de donner le contrôle de la boucle d'événements.Dans votre exemple, je pense que le bon façon serait de faire un
yield None
ou, de manière équivalente, une simpleyield
, plutôt que d'unyield from asyncio.sleep(0.001)
:Coroutines sont juste un bon vieux Python générateurs.
En interne, le
asyncio
boucle d'événement tient un registre de ces générateurs et des appelsgen.send()
sur chacun d'eux un par un dans une boucle sans fin. Chaque fois que vousyield
, l'appel àgen.send()
complète et la boucle peut se déplacer sur. (Je suis le simplifier; jetez un oeil autour de https://hg.python.org/cpython/file/3.4/Lib/asyncio/tasks.py#l265 pour le code)Cela dit, j'allais encore le
run_in_executor
route si vous avez besoin de faire l'UC calcul sans partage de données.None
semble être plus élégant que d'utiliserasyncio.sleep()
partout...)asyncio.sleep(0)
. Voir cette discussion.