踩坑实录:Python 异步任务突然不执行,排查了整整一下午
踩坑实录:Python 异步任务突然不执行,排查了整整一下午
今天分享一个真实踩坑经历——asyncio 任务「静默失败」的问题,坑得我一下午没喘上气。
问题现象
项目里有一段异步代码,用来处理一批 HTTP 请求:
import asyncio
import aiohttp
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.text()
async def main():
urls = [
'https://httpbin.org/delay/2',
'https://httpbin.org/delay/3',
'https://httpbin.org/delay/1',
]
tasks = [fetch(url) for url in urls]
results = await asyncio.gather(tasks) # ❌ 这里有问题
print(results)
asyncio.run(main())
运行之后,程序直接退出了,没有任何输出,也没有报错。
排查过程
第一反应是「是不是 asyncio.run() 用错了」?换成 loop.run_until_complete(),问题依旧。
加 try/except,也抓不到异常。
然后用 asyncio.gather(..., return_exceptions=True) 试了一下——终于有输出了:
[TypeError("'list' object is not awaitable"), ...]
原来问题在这里:asyncio.gather() 接收的是「协程对象」,不是「协程对象的列表」。
我传的是 tasks(一个 list),但 gather 的签名是 gather(*coros, ...),需要解包。
根因分析
正确写法应该是:
results = await asyncio.gather(*tasks) # ✅ 解包列表
而我原来写的是:
results = await asyncio.gather(tasks) # ❌ tasks 是一个 list,不是 coroutine
gather 把 tasks(这个 list)当成一个协程去 await,自然会报 TypeError。
但为什么没有报错?因为 asyncio.run() 在捕获到异常后,如果没有被正确 print,异常会被吞掉(特别是在某些 IDE 或运行环境下)。
解决方案
- 永远用
return_exceptions=True,至少能看到异常:
results = await asyncio.gather(*tasks, return_exceptions=True)
for r in results:
if isinstance(r, Exception):
print(f"任务失败: {r}")
- 给每个协程单独加 try/except,别让一个任务炸掉影响全部:
async def safe_fetch(url):
try:
return await fetch(url)
except Exception as e:
print(f"{url} 失败: {e}")
return None
- 如果用 Python 3.11+,优先用
asyncio.TaskGroup,异常传播更清晰:
async with asyncio.TaskGroup() as tg:
tasks = [tg.create_task(fetch(url)) for url in urls]
# 这里自动 wait,异常会正确抛出
经验总结
asyncio.gather(*coros)别忘了*解包,这是最常见的低级错误- 异步代码的异常容易被「吞掉」,调试时一定加
return_exceptions=True - Python 3.11 的
TaskGroup比gather更安全,新项目建议直接用 - 异步 bug 最难查的就是「没报错但结果不对」——遇到这种情况,先怀疑异常被吞了
*你有没有踩过类似的 asyncio 坑?欢迎评论区交流 *

浙公网安备 33010602011771号