【问题标题】:Python asyncio confused about await and tasksPython asyncio 对等待和任务感到困惑
【发布时间】:2019-05-22 21:19:52
【问题描述】:

在这里完成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())

我对@9​​87654323@最初的理解是阻塞当前函数的执行,等待异步函数返回。

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

进一步调查,通过在say_after 中添加额外的print,在我看来协程直到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

【问题讨论】:

    标签: python python-asyncio


    【解决方案1】:

    你对 await 的理解是正确的。它确实会暂停 main 函数的执行。

    关键是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

    【讨论】:

    • 好吧,我知道这是任务,但如果是任务,await 会改变其行为似乎很奇怪?
    • 你能澄清一下你认为它的行为变化吗? await 让 main 等待直到异步函数 say_after 返回。
    • 协程直到await task1才启动,所以它不像thread.join
    • 创建任务时从asyncio.create_task(say_after(1, 'hello'))开始。尝试运行没有await 行的代码,您会发现程序仍然打印出hello world
    • 看看我的最新更新,打印Received word 后打印Started timestamp
    【解决方案2】:

    当您将协程封装在 Task(或 Future)对象中时,协程就可以开始工作了,因此当事件循环在第一次等待时开始运行时,task1 和 task2 都在运行。

    为了更清楚,要执行一个协程你需要两件事:
    1) 封装在未来对象(任务)中的协程,使其可等待
    2) 一个正在运行的事件循环

    在您的示例中,执行工作如下:
    1 - create_task1
    2 - 创建任务2
    3 - 等待任务1
    4 - 等待任务 1 的睡眠
    5 - 等待任务 2 的睡眠

    现在 task1 和 task2 都在睡觉,所以假设 task1 是第一个完成的(睡一会儿)

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

    现在循环结束

    正如您所说,当您等待时执行停止,但让我说它仅在当前的“执行流程”中停止,当您创建未来(任务)时,您会创建另一个执行流程,而 await 只是切换到当前的执行流程。 最后一个解释在术语上并不完全正确,但有助于使其更清晰。

    我希望我很清楚。 P.S.:对不起我的英语不好。

    【讨论】:

    • create_task() 不会立即启动协程,它只会启动到await task1,但我的理解是awake task1 应该阻止执行await task2 直到task1 完成。
    • 我认为您的理解是正确的,但只是从调用await的函数来看,从全局的角度来看await只是将执行切换到另一个任务,这就是为什么使用协程进行多任务处理是合作的而不是先发制人的。
    • 我无法理解await task1 块开始执行await task2 的事实,如果await task2 未在当前函数上下文中执行,那么task2 如何与task1 同时运行.
    【解决方案3】:

    好的@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 语句才会运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-27
      • 1970-01-01
      • 2013-08-20
      • 2023-03-17
      相关资源
      最近更新 更多