简体   繁体   中英

Python asyncio: how are tasks scheduled?

I'm new to Python asyncio and I'm doing some experiments. I have the following code:

async def say_after(n, s):
    await asyncio.sleep(n)
    print(s)

async def main():
    task1 = asyncio.create_task(say_after(2, 'a'))
    task2 = asyncio.create_task(say_after(1, 'b'))
    await task1
    print('x', flush=True)
    await task2
    print('y', flush=True)

asyncio.run(main())

And the output:

b
a
x
y

I don't understand the order here. Can someone help explain? Especially why x comes after b and a?

Executing say_after (without await ) creates a coroutine object, but does not start it yet .

If you await on the coroutine object, then you are executing the coroutine until the Python encounters one of await or return (or end of function) in the coroutine. "Executing" here means transforming the coroutine into a Task object and put that object in the async loop.

However, create_task immediately 'starts' the coroutine and put them tasks in the async loop (though, because this is async instead of parallel, execution does not actually begin until Python encounters await in main() ).

In your situation, as soon as Python sees the await task1 , it 'leaves' main() and loops among task1 and task2 back and forth (because both tasks have been put in the async loop by create_task) until task1 is completed (because task1 is the one being await-ed on). Because task2 had scheduled itself to wait for a shorter time, it finishes first. About 1 second later, task1 completes, and only then execution returns to main() (because, remember, it was task1 that main() had been await-ing on).

At this point, both task1 and task2 has completed; the await task2 line practically does nothing; it's just "wrapping up" task2 and (almost) immediately returns.

Especially why x comes after b and a?

b is printed in task2 about 1 second after the start of main , and a is printed in task1 about 2 seconds after the start of main . The await task1 waits for task1 , and so waits about 2 seconds. So by the time await task1 completes, both b and a would have been printed.

(The "about"s in the above are deliberate... there would be variations, but in most situations, they would be small)

await task1 basically says that do not execute the next lines of code until task1 is finished (ie the execution of say_after(2, 'a') ).

task1 takes longer than task2 to execute, so by the time you "awaited" task1 , task2 is already finished executing. So putting await task2 below await task1 is somewhat useless here. If you swap these two lines, the output will be different.

That's why 'b' is printed before 'a' . And not until 'a' is printed, 'x' and 'y' could be printed.

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