在Python类中使用asyncio和aiohttp遇到的问题
这种情况我之前踩过坑!大概率是你在类里处理异步逻辑的姿势不对——函数式写法时你可能直接用asyncio.run()把协程跑起来了,但放到类里后,要么是没正确触发异步方法执行,要么是事件循环的管理出了问题,导致程序卡在那儿没推进。下面给你拆解常见问题和解决办法:
1. 用同步方式直接调用异步方法
这是最容易犯的错误:在类的同步方法里直接调用self.xxx_async(),这只会返回一个协程对象,根本没真正执行。比如:
# 错误示例 class AsyncScraper: async def fetch_page(self): async with aiohttp.ClientSession() as session: return await session.get("https://example.com") def start(self): # 这里只是创建了协程,没有调度执行 self.fetch_page()
修复方式:要么在同步方法里用asyncio.run()包裹异步调用,要么把整个启动逻辑改成异步:
# 方案1:同步启动 def start(self): asyncio.run(self.fetch_page()) # 方案2:异步启动(更推荐,适合复杂场景) async def start_async(self): await self.fetch_page() # 然后在Main.py里这样调用 asyncio.run(scraper.start_async())
2. 类内部事件循环管理混乱
如果你的类自己维护了事件循环(比如在__init__里创建loop = asyncio.get_event_loop()),但Main.py里又用asyncio.run()启动了新循环,就会导致协程被挂在未运行的循环里,程序直接锁死。
修复方式:统一让顶层的asyncio.run()管理事件循环,类里的所有异步方法只负责定义逻辑,用await调用即可,不要自己创建/管理循环。
3. aiohttp ClientSession未正确管理
如果把ClientSession作为类的实例属性,但没在退出时关闭,可能导致资源泄漏,进而让程序卡住。比如:
# 有隐患的写法 class AsyncScraper: def __init__(self): self.session = aiohttp.ClientSession() # __init__是同步方法,这里创建异步对象有风险 async def fetch(self, url): return await self.session.get(url)
修复方式:用异步方法初始化Session,并且在使用完后关闭:
class AsyncScraper: def __init__(self): self.session = None async def init_session(self): self.session = aiohttp.ClientSession() async def fetch(self, url): if not self.session: await self.init_session() return await self.session.get(url) async def cleanup(self): if self.session: await self.session.close() # Main.py里的调用逻辑 async def main(): scraper = AsyncScraper() try: resp = await scraper.fetch("https://example.com") print(await resp.text()) finally: await scraper.cleanup() asyncio.run(main())
4. 异步方法里混入同步阻塞代码
如果你的类方法里不小心用了time.sleep()、requests.get()这类同步阻塞操作,会直接把整个事件循环卡死,看起来就像程序无响应。
修复方式:把所有阻塞操作换成异步版本——比如用asyncio.sleep()替代time.sleep(),用aiohttp替代requests。
# AsyncFetcher.py import asyncio import aiohttp class AsyncFetcher: def __init__(self): self._session = None async def _setup(self): # 异步初始化资源 if not self._session: self._session = aiohttp.ClientSession() async def fetch_json(self, url): await self._setup() async with self._session.get(url) as resp: resp.raise_for_status() return await resp.json() async def close(self): # 清理资源 if self._session: await self._session.close() # Main.py from AsyncFetcher import AsyncFetcher async def main(): fetcher = AsyncFetcher() try: todo = await fetcher.fetch_json("https://jsonplaceholder.typicode.com/todos/1") print(f"Fetched todo: {todo}") finally: await fetcher.close() if __name__ == "__main__": asyncio.run(main())
如果还是排查不出问题,可以开启异步调试模式:asyncio.run(main(), debug=True),这样能看到协程的调度状态,更容易找到卡住的原因。
内容的提问来源于stack exchange,提问作者antfuentes87

