`asyncio.wait` 传递协程时的混乱

`asyncio.wait` confusion when passed a coroutine

asyncio.wait 说:

Passing coroutines objects to wait() directly is deprecated as it leads to confusing behavior.

并将此作为令人困惑的行为:

wait() schedules coroutines as Tasks automatically and later returns those implicitly created Task objects in (done, pending) sets. Therefore the following code won’t work as expected: [I modified the code snippet below so that it runs]

async def foo():
    return 42

async def main():
    coro = foo()
    done, pending = await asyncio.wait({coro})

    if coro in done:
        # This branch will never be run!
        print('yay!')

asyncio.run(main())

刚开始学习asyncio,所以不是很了解。有人可以解释一下吗?

文档中给出的示例代码是:

coro = foo()
done, pending = await asyncio.wait({coro})

if coro in done:
    # This branch will never be run!

这段代码给出意外结果的原因如下:

  • coro 是协程对象。
  • 当它被传递给asyncio.wait时,它会自动从中创建一个Task对象(这与协程对象不同),如coro_task = create_task(coro)(参见create_task).
  • asyncio.wait完成后,return有两组:
    • 一组已经完成的任务
    • 一组尚未完成的任务

所以在这种情况下,它将 return 一组包含 coro_task 和一组为空。

请注意,原始协程对象 coro(与 coro_task 不同)不包含在任何集合中,因此请检查它是否在“完成”集合中,毫无意义 - 它永远不会被包含,即使相应的任务 coro_task 已经完成。

解决方法是为 asyncio.wait coro 外部 创建 Task 对象,这将允许测试 相同 对象包含在一个或另一个 returned 集合中,以确定任务是否完成。

在从 3.8 开始的 Python 版本中,如果您将协程传递给 asyncio.wait,您将收到弃用警告,并且从 3.11 版本开始,这将是一个错误,即您被迫使用“固定”代码。