python asyncios create_task和等待函数

时间:2018-10-10 10:05:28

标签: python asynchronous

我试图理解python的asynico模块,并在https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task遇到了以下代码段

class Generic<T extends Number> {
    Function<T, Boolean> f = i -> true;

    public Boolean use(T n) {
        return f.apply(n);
    }
}

事实证明,可以简单地删除import time import asyncio 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('started at', time.strftime('%X')) # Wait until both tasks are completed (should take # around 2 seconds.) await task1 await task2 print('finished at', time.strftime('%X')) asyncio.run(main()) (或await task2,但不能同时删除两者),并且代码看起来完全一样。我觉得这很违反直觉,这是怎么回事? 谢谢您的宝贵时间。

2 个答案:

答案 0 :(得分:1)

await不会在任务中启动协同程序,它只是告诉协同程序main wait 来等待这两个程序。事件循环从run_until_complete隐式开始,这意味着它将等待传递给它的Coro(main)完成。依靠自己的生命来延长自己的生存期(通过await)足够长的时间以确保从其内部创建的任务能够完成。

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))
    task2 = asyncio.create_task(
        say_after(2, 'world'))
    #await task1
    #await task2
    print(asyncio.all_tasks(asyncio.get_event_loop()))

# will print (added line breaks and shortened paths for readibility):
{
    <Task pending coro=<main() running at C:/Users/.../lmain.py:17> cb=[_run_until_complete_cb() at C:...\lib\asyncio\base_events.py:150]>, 
    <Task pending coro=<say_after() running at C:/Users/.../lmain.py:5>>, 
    <Task pending coro=<say_after() running at C:/Users/.../lmain.py:5>>
}

如您所见,所有三个花冠都没有await运行任何东西。只是两个say_after花费的时间比main隐含地控制事件循环运行多长时间的main长。

如果您让async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print('started at', time.strftime('%X')) #await task1 #await task2 await asyncio.sleep(5) print('finished at', time.strftime('%X')) # output started at 15:31:48 hello world finished at 15:31:53 等待循环中的工作足够长的时间,则两项任务都将完成:

await

因此,当您测试注释以上task1和/或task2的{​​{1}}时,完成哪个任务(如果有)基本上只是时间问题,主要受以下因素影响:硬件,操作系统以及可能的运行时(即IDE与Shell)。

P.S。任务仅包含three statespendingcancelledfinished。每个任务在创建后立即处于状态pending,并保持该状态,直到包装在其中的协同程序终止(以任何方式终止)或被控制它的事件循环取消为止。

答案 1 :(得分:1)

您提出了三种不同的情况:

  1. 没有await条语句(全部注释掉)
  2. 仅使用await task1(注释掉第二个)
  3. 仅使用await task2(注释掉第一个)

这是您的脚本;仅出于说明目的,将task2上的睡眠时间延长一点。

# tasktest.py
import time
import asyncio

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(3, 'world'))

    print('started at', time.strftime('%X'))
    await task1
    # await task2
    print('finished at', time.strftime('%X'))

asyncio.run(main())

1。没有await语句

这是asyncio.run()的鲜肉:

loop = events.new_event_loop()
try:
    events.set_event_loop(loop)
    loop.set_debug(debug)
    return loop.run_until_complete(main)   # < -----
finally:
    try:
        _cancel_all_tasks(loop)            # < -----
        loop.run_until_complete(loop.shutdown_asyncgens())
    finally:
        events.set_event_loop(None)
        loop.close()

重要的是,仅 循环关心main()已完成,然后取消了与正在运行的事件循环关联的所有其他任务。 (每个任务在指定后都是tied to个事件循环。)

如果您定义main()时没有任何await语句,则create_task()计划要执行的任务,但是main()不会等待其中任何一个完成。< / p>

2。 await task1

设置:

await task1
# await task2

输出:

(base_py37) $ python3 tasktest.py 
started at 11:06:46
hello
finished at 11:06:47

这两个任务都从待处理变为正在运行,但只有task1完成,因为main()仅等待大约1秒钟的任务,而该时间不足以使task2运行。 sup> * (请注意,main()仅花费1秒。)

3。 await task2

设置:

# await task1
await task2

输出:

(base_py37) $ python3 tasktest.py 
started at 11:08:37
hello
world
finished at 11:08:40

这两个任务都从待处理变为正在运行,现在task1task2都完成了,因为main()等待的任务大约需要3秒钟,足以使两个任务都能运行完成。


*这至少适用于我的设置(Mac OSX,...),但是如此处其他答案所述,在其他设置上,计时可能会有所不同,并且如果任务运行时间相似,两者都可能会在第2种情况下运行。