Python asyncio对等待和任务感到困惑

时间:2019-05-22 21:19:52

标签: python python-asyncio

在此处完成newb的内容,其中包含有关以下示例的Asycnio Tasks

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

我对await的最初理解是,它阻止了当前函数的执行,并等待异步函数返回。

但是在这种情况下,两个协程同时执行,这与我对await的理解不太吻合。有人可以解释一下吗?

进一步的调查是,在print中添加了额外的say_after,在我看来,协程直到await出现才开始...

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

打印

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

3 个答案:

答案 0 :(得分:1)

您对等待的理解是正确的。确实会暂停主要功能的执行。

关键是asyncio.create_task()创建并计划任务。

因此say_after函数在此处开始运行:

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

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

而不是您等待的时间。

参见此处:https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task

答案 1 :(得分:1)

将协程封装在Task(或Future)对象中时,协程已准备就绪,因此当事件循环在第一次等待时开始运行时,task1和task2都在运行。

要使其更清晰,执行协程,您需要做两件事:
1)将协程封装在将来的对象(任务)中以使其可以等待
2)正在运行的事件循环

在您的示例中,执行方式如下:
1-create_task1
2-create_task2
3-等待任务1
4-等待任务1的睡眠
5-等待任务2的睡眠

现在task1和task2都在睡眠,因此,假设task1是第一个完成的睡眠(一段时间)。

6-任务1的打印
7-等待任务2
8-task2的打印

现在循环结束

正如您所说,等待时执行会停止,但我要说的是,它会停止在当前的“执行流”中,当您创建future(Task)时,您会创建另一个执行流,而等待会切换到当前的执行流程。 最后的解释在意义上并不完全正确,但有助于使其更清楚。

我希望我很清楚。 附注:对不起,我的英语不好。

答案 2 :(得分:0)

确定@tsuyoku和@Fanto答案都是正确的,这个答案只是对现有答案的补充,对我来说,我不明白的重要一点是从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())

打印

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

最初的误解是,东西需要花费一些时间来运行任务,而我的问题中的原始打印内容误导了我,认为该任务要到await语句后才能运行。