阅读asyncio 文档,我意识到我不理解一个非常基本和基本的方面:直接等待协程与等待包含在任务中的同一个协程之间的区别。
在文档示例中,对say_after协程的两次调用在没有等待时顺序运行create_task,而在包装中时并发运行create_task。所以我明白这基本上就是区别,这是一个非常重要的区别。
然而让我困惑的是,在我到处阅读的示例代码中(例如展示如何使用aiohttp),有很多地方等待(用户定义的)协程(通常在其他一些用户定义的协程中间)而没有被包裹在一个任务中,我想知道为什么会这样。确定何时应将协程包装在任务中的标准是什么?
use*_*342 16
确定何时应将协程包装在任务中的标准是什么?
当您希望协程在后台有效运行时,您应该使用任务。您看到的代码只是直接等待协程,因为它需要它们按顺序运行。例如,考虑一个 HTTP 客户端发送请求并等待响应:
# these two don't make too much sense in parallel
await session.send_request(req)
resp = await session.read_response()
Run Code Online (Sandbox Code Playgroud)
在某些情况下,您希望操作并行运行。在那种情况下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
resp1 = await dl1
resp2 = await dl2
# or, shorter:
resp1, resp2 = asyncio.gather(session.get(url1), session.get(url2))
Run Code Online (Sandbox Code Playgroud)
如上所示,也可以等待任务。就像等待一个协程一样,它会阻塞当前的协程,直到任务驱动的协程完成。与线程类比,等待任务大致相当于 join()-ing 线程(除非您返回返回值)。另一个例子:
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':
# ...
Run Code Online (Sandbox Code Playgroud)
总之,如果您需要协程的结果才能继续,您应该等待它而不创建任务,即:
# 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())
Run Code Online (Sandbox Code Playgroud)
继续线程类比,创建一个任务只是为了立即等待它就像运行t = Thread(target=foo); t.start(); t.join()而不是仅仅foo()- 低效和冗余。
| 归档时间: |
|
| 查看次数: |
2254 次 |
| 最近记录: |