asyncio.create_task() 做啥?

Posted

技术标签:

【中文标题】asyncio.create_task() 做啥?【英文标题】:What does asyncio.create_task() do?asyncio.create_task() 做什么? 【发布时间】:2020-10-13 03:09:55 【问题描述】:

asyncio.create_task() 是做什么的?我查看了文档,似乎无法理解。让我困惑的一段代码是这样的:

import asyncio

async def counter_loop(x, n):
    for i in range(1, n + 1):
        print(f"Counter x: i")
        await asyncio.sleep(0.5)
    return f"Finished x in n"

async def main():
    slow_task = asyncio.create_task(counter_loop("Slow", 4))
    fast_coro = counter_loop("Fast", 2)

    print("Awaiting Fast")
    fast_val = await fast_coro
    print("Finished Fast")

    print("Awaiting Slow")
    slow_val = await slow_task
    print("Finished Slow")

    print(f"fast_val, slow_val")

asyncio.run(main())

这给出了以下输出:

001 | Awaiting Fast
002 | Counter Fast: 1
003 | Counter Slow: 1
004 | Counter Fast: 2
005 | Counter Slow: 2
006 | Finished Fast
007 | Awaiting Slow
008 | Counter Slow: 3
009 | Counter Slow: 4
010 | Finished Slow
011 | Finished Fast in 2, Finished Slow in 4

我不太明白这是如何工作的。

    不应该slow_task 直到完成后才能运行 fast_coro,因为它从未在 asyncio.gather 中使用过 方法? 我们为什么要await slow_task? 为什么在协程似乎已经启动后打印Awaiting Slow? 什么是真正的任务?我知道gather 正在做的是安排一个 任务create_task 据说会创建一个任务。

我们将不胜感激。谢谢!

值得一提的是,我对 Futures 知之甚少。

【问题讨论】:

如日志所示,立即执行了一个任务 【参考方案1】:

asyncio.create_task() 是做什么的?

它提交协程以“在后台”运行,即与当前任务和所有其他任务同时运行,在await 点之间切换。它返回一个称为“任务”的可等待句柄,您也可以使用它来取消协程的执行。

它是 asyncio 的核心原语之一,相当于启动线程的 asyncio。 (同理,使用await 等待任务相当于加入线程。)

fast_coro完成之前,slow_task是否应该无法运行

不,因为您明确使用create_task 在后台启动slow_task。你有没有写过类似的东西:

    slow_coro = counter_loop("Slow", 4)
    fast_coro = counter_loop("Fast", 2)
    fast_val = await fast_coro

...确实slow_coro 不会运行,因为还没有人将它提交给事件循环。但create_task 正是这样做的:将其提交到事件循环以与其他任务同时执行,切换点是任何await

因为它从未在 asyncio.gather 方法中使用过?

asyncio.gather 并不是在 asyncio 中实现并发的唯一方法。它只是一个实用函数,可以更轻松地等待多个协程全部完成,并同时将它们提交到事件循环。 create_task 只是提交,它可能应该被称为 start_coroutine 或类似的名称。

我们为什么要等待slow_task

我们没有必须这样做,它只是用来等待两个协程干净地完成。代码也可以等待asyncio.sleep() 或类似的东西。立即从main()(和事件循环)返回,而某些任务仍处于待处理状态也可以正常工作,但它会打印一条警告消息,指示可能存在错误。在停止事件循环之前等待(或取消)任务更简洁。

什么是真正的任务?

它是一个异步构造,用于在具体事件循环中跟踪协程的执行。当你调用create_task 时,你提交了一个协程来执行并接收一个句柄。当您真正需要结果时,您可以等待此句柄,或者如果您不关心结果,则永远不能等待它。这个句柄就是task,它继承自Future,使得它可以等待,并且还提供了基于回调的底层接口,例如add_done_callback

【讨论】:

谢谢!我只有两个后续问题。 @ArnabMukherjee 它在同一个线程中执行,所以我猜它是并发的,而不是真正的并行。 @ArnabMukherjee 没有真正的“背景”,它们会在您等待某些东西时运行,并且它们已准备好运行。这些 cmets 应作为单独的问题发布;另见this one。 @aleks224 如果您引用的代码与问题中的代码完全相同,那就是。一般来说,asyncio 不保证可运行任务的执行顺序,但这里的代码等待的是协程,而不是任务。直接等待的协程中的代码会立即执行(不屈服于事件循环)直到第一次暂停。由于await fast_coro 和第一次打印之间没有暂停,它总是在来自另一个任务的任何内容之前。 @aleks224 保证立即执行等待的协程直到暂停,尽管您可能会发现在文档中很难找到章节和诗句。基本上它遵循awaityield from 方面都是specified and implemented,这就是yield from 必须如何表现以满足PEP 380 的重构原则

以上是关于asyncio.create_task() 做啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Uvicorn 和 asyncio.create_task() 将任务置于后台?

python-asyncio

如何将协程添加到正在运行的 asyncio 循环中?

gevent常用用法

python实现异步编程 python协程

JMS 能做啥 REST 不能做啥