asyncio.run()不可在已运行事件循环中嵌套调用;aiohttp.ClientSession()须复用并显式关闭;asyncio.gather()适合批量等待,create_task()适合动态控制;JSON解析等阻塞操作应交由线程池处理。

asyncio.run() 不能在已运行的事件循环中嵌套调用
直接在 Jupyter、IPython 或某些 Web 框架(如 FastAPI 的依赖函数)里写 asyncio.run(main()),会报 RuntimeError: asyncio.run() cannot be called from a running event loop。这是因为这些环境本身已启动了事件循环。
实操建议:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 在普通 Python 脚本(.py 文件)中,用
asyncio.run()是安全且推荐的入口方式 - 在 Jupyter 中,改用
await main()(前提是main是 async 函数) - 若需兼容两种环境,可加判断:
try: asyncio.run(main()) except RuntimeError: await main()
aiohttp.ClientSession() 必须复用,不能每次请求都新建
常见错误是把 aiohttp.ClientSession() 写在异步函数内部、甚至 for 循环里,导致连接池失效、DNS 重复解析、TCP 连接激增,最终触发 ClientConnectorError 或超时。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 将
session作为参数传入抓取函数,或用async with在外层统一管理生命周期 - 设置合理连接池参数,比如
aiohttp.TCPConnector(limit=100, limit_per_host=30),避免单域名压垮目标服务 - 务必显式关闭 session:不光靠
async with,在异常路径下也要确保await session.close()
asyncio.gather() 和 asyncio.create_task() 的适用边界
两者都能并发执行协程,但行为差异直接影响错误处理和资源控制。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
asyncio.gather(*tasks, return_exceptions=True)批量等待全部完成,适合“全量抓取+统一分析”场景;设return_exceptions=True防止一个失败中断全部 - 用
asyncio.create_task()更适合需要动态启停、按需 await、或配合asyncio.wait_for()控制单个超时的场景 - 避免混合使用:不要在
gather里传create_task()结果再 await —— 容易引发Task was destroyed but it is pending!
JSON 解析和数据清洗必须放在协程内,别丢给同步函数阻塞事件循环
像 json.loads()、pandas.read_json()、正则匹配大量文本等操作,表面看很快,但在高并发下会因 GIL 或 CPU 密集拖慢整个事件循环,表现为吞吐量上不去、响应延迟突增。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 轻量 JSON 解析(如结构固定、体积小)可用
json.loads(),没问题 - 大响应体或复杂清洗逻辑,改用
loop.run_in_executor(None, sync_func, data)扔进线程池,比如:result = await loop.run_in_executor(None, json.loads, response_text)
- 注意
None表示使用默认 ThreadPoolExecutor;CPU 密集型任务应换concurrent.futures.ProcessPoolExecutor,但要小心进程间序列化开销

















