asyncio.wait_for 未按预期超时

Sem*_*nic 5 python python-asyncio fastapi

我正在调试 FastAPI 应用程序,并且遇到了与本文中提到的问题类似的问题:asyncio.wait_for对应该超时的调用却没有超时:

try:
    await wait_for(completion_event.wait(), 1.0)
except TimeoutError:
    logging.info("timeout")
    return SubmissionResult(post_id=post_id, language_check_pending=True)
Run Code Online (Sandbox Code Playgroud)

此代码片段是 FastAPI 的 POST 请求处理程序的一部分。这里,completion_event是一个asyncio.Event对象。我可以在带有 的行上放置一个断点wait_for,观察它卡住超过 1 秒的时间,然后直接移过该except块。毫无疑问,这wait_for并没有达到预期的效果。

我不知道为什么它会这样。此时,我开始怀疑 FastAPI 的内部结构,因为它使用uvloop作为“更快的异步替代品”。但我不知道如何检验这个假设,更不知道如何解决这个问题(如果确实如此)。

有什么建议么?

Pau*_*ius 2

一种可能的解释是completion_event.wait()在时间延迟过去之前引发异常。取消封闭任务将被视为异常。下面的程序说明了这一点:

import asyncio

async def fail():
    await asyncio.sleep(0.5)
    # raise ValueError("Half a second")
    await asyncio.sleep(0.7)
    
async def cancel_point_three(task):
    await asyncio.sleep(0.3)
    task.cancel()
    
async def main():
    task = asyncio.create_task(fail())
    # asyncio.create_task(cancel_point_three(task))
        
    try:
        await asyncio.wait_for(task, 1.0)
    except asyncio.TimeoutError:
        print("Timed out")
    else:
        print("Did not time out")
        
asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)

按原样运行该程序,它将按预期打印“超时”。去掉前面的注释raise ValueError,代码就会一直拖到最后,不会打印“Timed out”。异常发生在超时之前。

如果您删除 前面的注释,也会发生类似的情况asyncio.create_task(cancel_point_three(task))。即使方法fail()没有引发任何异常,也没有超时。

TimeoutError另请注意,标准库中有两个类。其中之一asyncio.TimeoutError,就是养大的那一位asyncio.wait_for。基本异常层次结构中还有一个 TimeoutError - 它是 OSError 的子类,但不是您想要的。