我目前正在我的一个使用异步的项目中使用aiohttp。在搜索了大量内存使用的原因后,我发现aiohttp似乎在后台创建线程。
我已经将我的代码分解为这个最小的代码,这表明了我的问题。
import asyncio
import aiohttp
from aiohttp import ClientSession
async def test1(link, session):
async with session.get(
link,
) as r:
print(r.status)
await asyncio.sleep(10)
async def test():
async with ClientSession(
cookie_jar=aiohttp.DummyCookieJar(),
) as session:
await asyncio.gather(test1("https://google.com", session))
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
loop.close()
当用ps -e -T |grep python3
运行这个程序时,我会得到以下输出,这很奇怪,因为它看起来像是创建了一个线程:
160304 160304 pts/5 00:00:00 python3
160304 160306 pts/5 00:00:00 python3
如果我将asyncio.gather更改为再使用一个test1函数并再次运行ps命令,我将获得三个线程:
160414 160414 pts/5 00:00:00 python3
160414 160416 pts/5 00:00:00 python3
160414 160417 pts/5 00:00:00 python3
这看起来很有问题,因为我的假设是aiohttp在单个线程中使用事件循环,这就是为什么我在程序开始时使用ThreadPoolExecutor
来启动指定数量的线程。如果aiohttp为每个session.get请求创建一个新线程,那么线程数量可能是X个指定的线程*当前运行的HTTP请求。
我使用的更多上下文:
- Python 3.8.10
- Ubuntu 20.04.3 LTS
我的主程序的目的是尽快保存X数量的域的HTML。目前的体系结构是使用ThreadPoolExecutor
来增加Y个线程,并在整个应用程序生命周期中使用它,然后每个线程都使用session.get和asyncio.gather同时发送Z个HTTP请求。这是错误的方法吗?我应该使用另一个Python库而不是aiohttp吗?线程与事件循环相结合是多余的吗?
我在网上搜索了一下,还没有找到这个问题的答案,所以我谦虚地向社区寻求任何智能输入。
asyncio总是在启动min(32, (os.cpu_count() or 1) + 4)
线程的情况下至少有一个线程池。
该池由asyncio
用于内部DNS查找。
此外,即使将aiohttp
设置为使用aiodns
进行DNS解析,默认的异步池仍然存在(而不执行任何操作(。
反过来,aiohttp
将默认线程池用于某些操作,主要用于本地文件处理。
例如,await session.post(url, data=open('filename', 'rb'))
读取用于在线程中发送的文件块;它有助于避免长时间阻塞呼叫。