1
votes

Python asyncio est confus à propos de l'attente et des tâches

Complétez newb ici, en lisant sur Asycnio Tasks a > qui a cet exemple:

started at 13:41:23
Received hello
Received world
hello
world
finished at 13:41:25

Ma compréhension initiale de await est qu'il bloque l'exécution de la fonction actuelle et attend le retour de la fonction asynchrone. p >

Mais dans ce cas, les deux coroutines sont exécutées simultanément, cela ne correspond pas bien à ma compréhension de await . Quelqu'un pourrait-il expliquer?

Enquête plus approfondie, en ajoutant des print supplémentaires dans say_after , il me semble que la coroutine ne démarre pas avant attendre arrive ...

import asyncio
import time

async def say_after(delay, what):
    print('Received {}'.format(what))
    await asyncio.sleep(delay)
    print(what)

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')}")

asyncio.run(main())

imprime

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

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')}")

asyncio.run(main())


0 commentaires

3 Réponses :


1
votes

Votre compréhension de await est correcte. Cela met en pause l'exécution de la fonction principale.

La clé est que asyncio.create_task () crée une tâche et la planifie.

La fonction say_after démarre ici:

task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

et non lorsque vous attendez.

Voir ici: https://docs.python.org/3 /library/asyncio-task.html#asyncio.create_task


6 commentaires

eh bien oui, je sais que c'est la tâche, mais ça semble bizarre que wait change son comportement si c'est une tâche?


Pourriez-vous clarifier comment vous pensez que cela change de comportement? await fait attendre le retour de la fonction asynchrone say_after .


la coroutine ne démarre pas avant wait task1 , donc ce n'est pas comme thread.join


Il commence à asyncio.create_task (say_after (1, 'hello')) , lorsque la tâche est créée. essayez d'exécuter le code sans les lignes wait , vous constaterez que le programme imprime encore hello world .


Jetez un œil à ma dernière mise à jour, elle imprime Mot reçu après l'impression de Horodatage de démarrage


La fonction say_after ne démarre pas à create_task , elle démarre à l'entrée suivante dans la boucle d'événements. C'est ce qui se passe (généralement) à la prochaine attente , indépendamment de ce qui est attendu.



1
votes

Lorsque vous encapsulez une coroutine dans un objet Task (ou Future), la coroutine est prête à fonctionner, donc lorsque la boucle d'événements commence à s'exécuter lors de la première attente, la tâche1 et la tâche2 sont en cours d'exécution.

Pour clarifier les choses, pour exécuter une coroutine, vous avez besoin de deux choses:
1) une coroutine incapsulée dans un futur objet (Task) pour la rendre attendu
2) une boucle d'événement en cours

Dans votre exemple, l'exécution fonctionne comme ceci:
1 - create_task1
2 - create_task2
3 - attendre la tâche1
4 - attendre le sommeil de la tâche1
5 - attendre le sommeil de la tâche2

maintenant, la tâche1 et la tâche2 sont en veille, donc, supposons que la tâche1 soit la première à terminer (dormir quelque temps)

6 - impression de la tâche1
7 - attendre la tâche2
8 - impression de la tâche2

maintenant la boucle se termine

Comme vous l'avez dit lorsque vous avez une attente, l'exécution s'arrête, mais laissez-moi vous dire que cela s'arrête juste dans le "flux d'exécution" actuel, lorsque vous créez une future (tâche), vous créez un autre flux d'exucution, et l'attente passe simplement à le flux d'exécution actuel. Cette dernière explication n'est pas tout à fait correcte dans le sens des termes mais aide à la rendre plus claire.

J'espère avoir été clair. P.S .: désolé pour mon mauvais anglais.


3 commentaires

create_task () ne démarre pas la coroutine immédiatement, elle ne démarre que jusqu'à wait task1 , mais je crois comprendre que awake task1 devrait bloquer l'exécution de attendez la tâche2 jusqu'à ce que la tâche1 soit terminée.


Je pense que votre compréhension est correcte mais juste du point de vue de la fonction qui appelle l'attente, d'un point de vue global l'attente, il suffit de passer l'exécution à une autre tâche, c'est pourquoi le multitâche avec coroutine est coopératif et non préventif.


Je ne peux tout simplement pas faire passer le fait que les blocs wait task1 commencent à exécuter wait task2 , si wait task2 n'est pas exécuté dans le contexte de la fonction actuelle, alors comment peut task2 s'exécuter simultanément avec task1 .



0
votes

OK Les réponses @tsuyoku et @Fanto sont correctes, cette réponse est juste pour compléter les réponses existantes, le point important pour moi que je ne pouvais pas saisir était l'exécution commencée sur create_task () :

started at 10:42:10
Received hello
Received world
hello
world
task 2 finished
task 1 finished
finished at 10:42:20

prints

import asyncio
import time

async def say_after(delay, what):
    print('Received {}'.format(what))
    await asyncio.sleep(delay)
    print(what)

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')}")
    await asyncio.sleep(10)

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task2
    print('task 2 finished')
    await task1
    print('task 1 finished')

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

le malentendu original est que les choses prennent un peu de temps pour exécuter les tâches, et l'impression originale dans mon La question m'a induit en erreur en pensant que la tâche ne s'exécute pas avant l'instruction wait .


0 commentaires