asyncio.create_task() 有什么作用?

Bea*_*der 29 python python-3.x async-await python-asyncio

有什么作用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())
Run Code Online (Sandbox Code Playgroud)

这给出了以下输出:

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
Run Code Online (Sandbox Code Playgroud)

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

  1. 是不是因为它从未在 方法中使用过而不能slow_task运行直到完成?fast_coroasyncio.gather
  2. 为什么我们必须这样做await slow_task
  3. 为什么Awaiting Slow在协程似乎已经启动后打印?
  4. 什么才是真正的任务?我知道gather正在做的是安排 任务。并且create_task据说创建了一个任务。

一个深入的答案将不胜感激。谢谢!

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

use*_*342 39

有什么作用asyncio.create_task()

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

它是 asyncio 的核心原语之一,相当于启动线程的 asyncio。(在同一个类比中,等待任务await相当于加入一个线程。)

不应该slow_task是不能运行,直到完成fast_coro

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

    slow_coro = counter_loop("Slow", 4)
    fast_coro = counter_loop("Fast", 2)
    fast_val = await fast_coro
Run Code Online (Sandbox Code Playgroud)

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

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

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

为什么我们要等待slow_task

我们不具备时,它只是用来等待协同程序都干净完成。代码也可以等待asyncio.sleep()或类似的东西。main()立即从(和事件循环)返回一些任务仍然未决也可以工作,但它会打印一条警告消息,表明可能存在错误。在停止事件循环之前等待(或取消)任务更简洁。

什么才是真正的任务?

它是一个 asyncio 构造,用于跟踪具体事件循环中协程的执行。当您调用 时create_task,您提交一个协程以供执行并收到一个句柄。您可以在实际需要结果时等待此句柄,或者如果您不关心结果,则永远无法等待它。这个句柄是task,它继承自Future,这使得它可以等待并且还提供了基于回调的低级接口,例如add_done_callback.

  • @BeastCoder `asyncio.gather(*list_of_tasks)` 将等待任务完成并返回各自协程的结果。`create_task` 返回一个任务,您可以 `await` 来获取正在运行的结果。如果它已经完成,不用担心,无论如何它都会保留结果。是的,为了方便起见,如果需要,“gather”会调用“create_task”——这是因为它支持除协程之外的可等待项。 (5认同)
  • @aleks224如果您所指的代码与问题中完全相同,那就是。一般来说,asyncio 不保证可运行任务的执行顺序,但这里的代码等待协程,而不是任务。直接等待的协程中的代码会立即执行(不会屈服于事件循环),直到第一次挂起。由于“await fast_coro”和第一次打印之间没有暂停,因此它将始终位于来自另一个任务的任何内容之前。 (5认同)
  • @ArnabMukherjee 没有真正的“背景”,它们在您等待某些东西时运行,并且它们已准备好运行。这些评论应作为单独的问题发布;另请参阅[这个](/sf/ask/3430395601/)。 (4认同)
  • @BeastCoder 差不多,是的。像 `tasks = [asyncio.create_task(c) for c in list_of_coros]; 之类的东西 results = [await t for t intasks]` 将是 `gather` 功能的一个很好的初步近似。然而,实现要复杂得多,因为“gather”对于如何传播异常、如何响应取消以及各种其他问题非常谨慎。 (3认同)
  • @aleks224 保证立即执行等待的协程直到暂停,尽管您可能会发现很难在文档中找到章节。基本上,它遵循“await”在“yield from”方面的[指定和实现](https://www.python.org/dev/peps/pep-0492/#await-expression),这就是如何`yield from` 的行为必须满足 [PEP 380](https://www.python.org/dev/peps/pep-0380/) 的_重构原则_。 (2认同)