简体   繁体   中英

Python asyncio confused about await and tasks

Complete newb here, reading about Asycnio Tasks which has this example:

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())

My original understanding of await is that it blocks the execution of current function and wait for the async function to return.

But in this case both coroutines are executed concurrently, it doesn't fit well my understanding of await . Could someone please explain?

Further investigation, by adding extra print in say_after , it seems to me the coroutine doesn't start until await happens...

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())

prints

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

Your understanding of await is correct. It does pause the execution of the main function.

The key is that asyncio.create_task() creates a task and schedules it.

So the say_after function starts running here:

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

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

and not when you await.

See here: https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task

When you encapsulate a coroutine in a Task (or Future) object the coroutine is ready to work, so when the event loop start running on the first await, both task1 and task2 are running.

Trying to make it clearer, to execute a coroutine you need two things:
1) a coroutine incapsulated in a future object(Task) to make it awaitable
2) a running event loop

In your example the execution works like this:
1 - create_task1
2 - create_task2
3 - await task1
4 - await sleep of task1
5 - await sleep of task2

now both task1 and task2 are sleeping so, suppose task1 is the first to finish (sleep some time)

6 - print of task1
7 - await task2
8 - print of task2

now the loop end

As you said when you got an await the execution stops, but let me say that stops just in the current "execution flow", when you create a future(Task) you create another exucution flow, and the await just switch to the current execution flow. This last explanation is not completely correct in sens of terms but help to make it clearer.

I hope i was clear. PS: sorry for my bad english.

OK Both @tsuyoku and @Fanto answers are correct, this answer is just to compliment the existing answers, the important bit for me that I could not grasp was the execution started on create_task() :

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())

prints

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

the original misunderstanding is that stuff takes a bit of time to run the tasks, and the original print out in my question misguided me to think the task doesn't run until the await statement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM