为什么我的进程列表在运行aiohttp时显示多个线程



我目前正在我的一个使用异步的项目中使用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'))读取用于在线程中发送的文件块;它有助于避免长时间阻塞呼叫。