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作为“更快的异步替代品”。但我不知道如何检验这个假设,更不知道如何解决这个问题(如果确实如此)。
有什么建议么?
一种可能的解释是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 的子类,但不是您想要的。