后台任务跑了十几秒就没了下文,既没有异常也没有结果,日志停在任务开始时那一行,进程还在正常处理别的请求。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

background = set()
def spawn(coro):
    t = asyncio.create_task(coro)
    background.add(t)
    t.add_done_callback(background.discard)

事件循环只保留任务的弱引用,create_task 的返回值没赋给任何变量,垃圾回收一触发任务对象就被回收,循环里对应的回调也一起消失。这一点先确认清楚,后面就不用反复试。

「create_task 引用丢失」相关的日志我一般连着看前后五分钟,单看一条报错经常判断不出是哪一步失败。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。

具体做法是,把任务对象存进一个模块级集合,完成回调里再移除;或者用 TaskGroup 管理一批任务的生命周期,退出时统一等待;关键任务把引用挂到长生命周期对象上,别让它落空。

改完照着核一遍,任务能完整跑到结束,日志里开始和结束两条记录都在,不再半途消失。

作者 9823189