【发布时间】:2022-01-20 11:46:13
【问题描述】:
我目前在我的一个使用 asyncio 的项目中使用 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 吗?线程与事件循环的结合是多余的吗?
我在网上四处搜索,但没有找到这个问题的答案,所以我谦虚地向社区征求任何明智的意见。
【问题讨论】:
-
您是否期望一个线程同时执行对
sleep的两个不同调用? -
我将端点更改为 localhost 并使用 sleep(10) 创建了一个名为 sleep.php 的文件;作为现在唯一的代码行,并且看到 asyncio 不会创建更多线程。我只是添加 asyncio.sleep 来演示这个问题,但在演示这个问题时,问题似乎出在我的编程逻辑中。我的假设是 asyncio 永远不会创建更多线程,但在最坏的情况下会阻塞。也许这就是我的实际程序出现问题的原因。您是否知道是否可以防止 asyncio 阻塞而不是创建更多线程?
-
我不太确定你在问什么。如果您打算同时调用两次
sleep,则需要两个线程。一个线程必须休眠 - 如果没有另一个线程,您将如何继续处理在该线程休眠时完成的异步 I/O? -
问题不在于睡眠部分,我只使用睡眠,所以我有足够的时间运行 ps -e -T |grep python3 并检查正在运行的线程数。问题是当我调用 session.get 时 asyncio 正在创建线程,我的假设是 asyncio 是一个单线程事件循环。
-
看起来我可以通过使用 asyncio 包中的 Semaphore 来缓解这个问题,不过感觉有点捷径。
标签: python-3.x multithreading concurrency python-asyncio aiohttp