我什么时候应该使用 asyncio.create_task?

Lor*_*zoC 7 python task coroutine python-3.x python-asyncio

我正在使用 Python 3.10,我对asyncio.create_task.

在下面的示例代码中,无论我是否使用asyncio.create_task. 似乎没有什么区别。

如何确定何时使用以及使用与不使用相比asyncio.create_task有何优势?asyncio.create_task

import asyncio
from asyncio import sleep

async def process(index: int):
    await sleep(1)
    print('ok:', index)

async def main1():
    tasks = []
    for item in range(10):
        tasks.append(asyncio.create_task(process(item)))
    await asyncio.gather(*tasks)

async def main2():
    tasks = []
    for item in range(10):
        tasks.append(process(item)) # Without asyncio.create_task
    await asyncio.gather(*tasks)

asyncio.run(main1())
asyncio.run(main2())
Run Code Online (Sandbox Code Playgroud)

Dan*_*erg 10

长话短说

如果您想立即create_task安排该协程的执行,但不一定要等待它完成而是先继续执行其他操作,那么使用 是有意义的。

解释

正如评论中已经指出的那样,asyncio.gather它本身将提供的可等待项包装在任务中,这就是为什么create_task在简单的示例中预先调用它们本质上是多余的。

来自gather文档:

如果任何可等待的 [...] 是协程,则会自动将其安排为任务。


话虽如此,您构建的两个示例并不等效

当您调用 时create_task,任务会立即安排在偶循环上执行。这意味着,如果在调用所有协程之后发生上下文切换create_task(如第一个示例中所示),则任意数量的协程都可以立即开始执行,而无需显式执行await它们。

来自create_task文档:(我的重点)

将 [...] 协程包装到 a 中Task安排其执行

相比之下,当您简单地创建协程(如第二个示例中所示)时,它们不会自行开始执行,除非您以某种方式安排它们的执行(例如,通过简单地await执行它们)。

如果您在创建和调用之间添加任何await(例如)以及一些有用的语句,您可以看到它的实际效果:asyncio.sleepgatherprint

from asyncio import create_task, gather, sleep, run


async def process(index: int):
    await sleep(.5)
    print('ok:', index)


async def create_tasks_then_gather():
    tasks = [create_task(process(item)) for item in range(5)]
    print("tasks scheduled")
    await sleep(2)  # <-- because of this `await` the tasks may begin to execute
    print("now gathering tasks")
    await gather(*tasks)
    print("gathered tasks")


async def create_coroutines_then_gather():
    coroutines = [process(item) for item in range(5)]
    print("coroutines created")
    await sleep(2)  # <-- despite this, the coroutines will not begin execution
    print("now gathering coroutines")
    await gather(*coroutines)
    print("gathered coroutines")


run(create_tasks_then_gather())
run(create_coroutines_then_gather())
Run Code Online (Sandbox Code Playgroud)

输出:

tasks scheduled
ok: 0
ok: 1
ok: 2
ok: 3
ok: 4
now gathering tasks
gathered tasks
Run Code Online (Sandbox Code Playgroud)
coroutines created
now gathering coroutines
ok: 0
ok: 1
ok: 2
ok: 3
ok: 4
gathered coroutines
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,in create_tasks_then_gatherbodyprocess是在调用之前执行的,gatherin create_coroutines_then_gatherit是在调用之后执行的。

所以说有没有用,create_task要根据情况而定。如果您只关心并发执行在代码中的特定点等待的协程,则调用create_task. 如果您想安排它们,然后继续做其他事情,而它们可能会也可能不会在后台做自己的事情,那么使用 是有意义的create_task

然而,要记住的一件重要的事情是,您只能确保您计划的任务实际上完全执行(如果您在某个时刻执行了 await它们)。这就是为什么你仍然应该让 await gather他们(或同等的人)真正等待他们最终完成。