Asyncio任务与协程

时间:2019-09-17 03:25:23

标签: python-asyncio

阅读asyncio documentation时,我意识到我不了解一个非常基本的方面:直接等待协程和将相同的协程包在任务中时要等待它们之间的区别。

在文档示例中,在没有say_after的情况下,对create_task协程的两个调用按顺序运行,而在用create_task包装时则同时运行。因此,我了解到这基本上是区别,并且是非常重要的。

但是,令我感到困惑的是,在我到处阅读的示例代码中(例如,显示如何使用aiohttp),在很多地方都等待着(用户定义的)协程(通常在中间)。其他一些用户定义的协程)而没有被包裹在任务中,我想知道为什么会这样。确定协程何时应包裹在任务中的标准是什么?

1 个答案:

答案 0 :(得分:2)

  

确定何时在任务中包装协程的标准是什么?

当您希望协程在后台有效运行时,应使用任务。您所看到的代码只是直接等待协程,因为它需要它们按顺序运行。例如,考虑一个HTTP客户端发送请求并等待响应:

# you wouldn't want these two to run in parallel
await session.send_request(req)
resp = await session.read_response()

在某些情况下,您想要操作要并行运行。在这种情况下,asyncio.create_task是合适的工具,因为它将执行协程的职责移交给了事件循环。这使您可以启动多个协程并在执行时闲置,通常等待它们中的一些或全部完成:

dl1 = asyncio.create_task(session.get(url1))
dl2 = asyncio.create_task(session.get(url2))
# run them in parallel and wait for both to finish
await asyncio.gather(dl1, dl2)
# or await dl1; await dl2

如上所示,也可以等待任务。就像等待协程一样,这将阻止当前协程,直到任务的协程完成为止。与线程类似,等待任务大致等同于对线程进行join()操作(除非您获得了实际值)。另一个例子:

queue = asyncio.Queue()

# read output from process in an infinite loop and
# put it in a queue
async def process_output(cmd, queue, identifier):
    proc = await asyncio.create_subprocess_shell(cmd)
    while True:
        line = await proc.readline()
        await queue.put((identifier, line))

# create multiple workers that run in parallel and pour
# data from multiple sources into the same queue
asyncio.create_task(process_output("top -b", queue, "top")
asyncio.create_task(process_output("vmstat 1", queue, "vmstat")

while True:
    identifier, output = await queue.get()
    if identifier == 'top':
        # ...

总而言之,如果您需要协程的结果才能继续进行,则应等待它而不创建任务,即:

# this is ok
resp = await session.read_response()
# unnecessary - it has the same effect, but it's
# less efficient
resp = await asyncio.create_task(session.read_reponse())

要继续进行线程类比,创建一个立即等待的任务就像运行Thread(target=foo).join()而不是运行foo()一样。