Asyncio doesn't execute the tasks asynchronous

2019-07-26 03:47发布

问题:

I'm playing around with asyncio module of Python and I don't know what's the problem with my simple code. It doesn't execute the tasks asynchronously.

#!/usr/bin/env python3    

import asyncio
import string    


async def print_num():
    for x in range(0, 10):
        print('Number: {}'.format(x))
        await asyncio.sleep(1)    

    print('print_num is finished!')    

async def print_alp():
    my_list = string.ascii_uppercase    

    for x in my_list:
        print('Letter: {}'.format(x))
        await asyncio.sleep(1)    

    print('print_alp is finished!')    


async def msg(my_msg):
    print(my_msg)
    await asyncio.sleep(1)    


async def main():
    await msg('Hello World!')
    await print_alp()
    await msg('Hello Again!')
    await print_num()    


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()

Here's the output when the script is called:

Hello World!
Letter: A
Letter: B
Letter: C
Letter: D
Letter: E
Letter: F
Letter: G
Letter: H
Letter: I
Letter: J
Letter: K
Letter: L
Letter: M
Letter: N
Letter: O
Letter: P
Letter: Q
Letter: R
Letter: S
Letter: T
Letter: U
Letter: V
Letter: W
Letter: X
Letter: Y
Letter: Z
print_alp is finished!
Hello Again!
Number: 0
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Number: 6
Number: 7
Number: 8
Number: 9
print_num is finished!

回答1:

You are calling the functions sequentially, so the code also executes sequentially. Remember that await this means "do this and wait for it to return" (but in the meantime, if this chooses to suspend execution, other tasks which have already started elsewhere may run).

If you want to run the tasks asynchronously, you need to:

async def main():
    await msg('Hello World!')
    task1 = asyncio.ensure_future(print_alp())
    task2 = asyncio.ensure_future(print_num())
    await asyncio.gather(task1, task2)
    await msg('Hello Again!')

See also the documentation of the asyncio.gather function. Alternatively, you could also use asyncio.wait.



回答2:

You're encountering a common source of confusion with await statements, which is that they behave sequentially for 'child' coroutines but they behave asynchronously for 'neighbouring' coroutines.

For example:

import asyncio

async def child():
    i = 5
    while i > 0:
        print("Hi, I'm the child coroutine, la la la la la")
        await asyncio.sleep(1)
        i -= 1

async def parent():
    print("Hi, I'm the parent coroutine awaiting the child coroutine")
    await child() # this blocks inside the parent coroutine, but not the neighbour
    print("Hi, I'm the parent, the child coroutine is now done and I can stop waiting")

async def neighbour():
    i = 5
    while i > 0:
        await asyncio.sleep(1)
        print("Hi, I'm your neighbour!")
        i -= 1

async def my_app():
    # start the neighbour and parent coroutines and let them coexist in Task wrappers
    await asyncio.wait([neighbour(), parent()])

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(my_app())

Which will output:

Hi, I'm the parent coroutine awaiting the child coroutine
Hi, I'm the child coroutine, la la la la la
Hi, I'm the child coroutine, la la la la la
Hi, I'm your neighbour!
Hi, I'm the child coroutine, la la la la la
Hi, I'm your neighbour!
Hi, I'm the child coroutine, la la la la la
Hi, I'm your neighbour!
Hi, I'm the child coroutine, la la la la la
Hi, I'm your neighbour!
Hi, I'm the parent, the child coroutine is now done and I can stop waiting
Hi, I'm your neighbour!

Process finished with exit code 0