asyncio.wait_for等待Event时aiohttp无法正常清理的原因咨询
问题原因与解决方案:
asyncio.wait_for + asyncio.Event导致aiohttp清理流程阻塞 核心问题分析
你遇到的问题本质是asyncio.wait_for与asyncio.Event.wait()的组合在取消时的特殊行为,导致aiohttp的清理流程无法正常调度执行:
Event.wait()的挂起逻辑:当Event未被触发时,wait()会将当前任务加入自身的_waiters列表,然后挂起在一个未完成的内部Future上。- 取消流程的竞态:当调用
t.cancel()取消任务时,wait_for会先触发e.wait()协程的取消,此时e.wait()需要执行finally块将任务从_waiters中移除。但这个操作与aiohttp的cleanup协程(执行ctx ending的代码)在事件循环中存在调度竞态——wait_for的取消处理占据了事件循环的调度权,导致cleanup代码无法被执行,最终事件循环直接退出,ctx ending日志无法输出。
而替换为asyncio.sleep(100)时,sleep的取消通过定时器触发,协程能立即响应并退出,事件循环可以顺利调度cleanup流程,所以日志正常输出。
解决方案
方案1:取消任务后等待其完成
修改ctx函数的finally块,在取消任务后等待任务处理完取消逻辑:
finally: logging.info("ctx ending") t.cancel() try: await t except asyncio.CancelledError: # 捕获任务的取消异常,不做额外处理 pass
这样能确保任务的取消流程完全结束,事件循环可以顺利执行cleanup代码,ctx ending和任务的取消日志都会正常输出。
方案2:使用Event.wait()的原生超时(Python 3.11+)
Python 3.11及以上版本中,asyncio.Event.wait()新增了timeout参数,可以直接替代asyncio.wait_for,避免两者组合带来的问题:
# 替换原wait_for代码 await e.wait(timeout=10000)
内容的提问来源于stack exchange,提问作者artesby
相关产品推荐
相关产品推荐

