You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Semaphore与TCPConnector搭配的作用及100K请求优化疑问

关于asyncio Semaphore与aiohttp并发控制的疑问解答

一、代码里的Semaphore到底起啥作用?

这段代码里的asyncio.BoundedSemaphore(value=100)是用来限制并发执行的——只有拿到信号量的fetch协程,才能进入session.get(url)的HTTP请求环节,最多同时有100个协程在处理请求相关操作。

二、你的判断对不对?

你提到的两点,一半准确,一半需要补充:

  • 关于“Semaphore多余”:从实际并发HTTP连接数来看,确实和aiohttp默认TCPConnector(限制100连接)的效果重叠,就算放开Semaphore,Connector也会卡住超出的连接请求。但某些场景下它并非多余,后面会具体说明。
  • 关于内存占用:你完全正确——不管加不加Semaphore,只要一次性创建100K个fetch协程并加入任务列表,这些协程对象都会占用内存,Semaphore只是让它们处于等待状态,不会减少内存消耗。

三、加这个Semaphore有额外优势吗?

有几个场景下它能发挥独特作用:

  • 控制全流程并发,不止HTTP连接:如果你的fetch函数里除了发请求,还有解析数据、写入文件等异步操作,Semaphore可以限制整个fetch流程的并发数,避免这些环节同时跑太多导致资源紧张。
  • 降低事件循环调度开销:一次性创建大量协程(比如100K个),事件循环要频繁调度这些等待状态的协程,会额外消耗系统资源。用Semaphore配合队列控制活跃协程数量,能减轻循环的调度压力。
  • 兼容性更强:如果后续切换到没有内置连接池限制的HTTP客户端,Semaphore可以直接保留,不用大幅修改代码逻辑。

四、处理100K个URL该怎么搞?

绝对不能直接按原代码执行,原因很明确:

  • 一次性创建100K个协程会瞬间占用大量内存,很可能导致内存溢出程序崩溃。
  • 事件循环调度大量等待状态的协程,会严重降低运行效率。

最优方案是用异步队列+固定数量的工作协程:

  1. 把所有URL提前放入一个异步队列中。
  2. 启动固定数量的工作协程(比如100个),每个协程循环从队列中取出URL进行处理,直到队列为空。
  3. 这种方式下,同时活跃的协程数就是你设置的工作协程数,内存占用可控,事件循环的调度压力也会小很多。

给你一个简单的示例代码:

import aiohttp
import asyncio

async def fetch(session, url, sema):
    async with sema, session.get(url) as response:
        return await response.text()

async def worker(session, queue, sema):
    while not queue.empty():
        url = await queue.get()
        try:
            html = await fetch(session, url, sema)
            print(html[:100])
        finally:
            queue.task_done()

async def main():
    # 模拟100K个URL
    urls = ['http://python.org', 'https://google.com', 'http://yifei.me'] * 33334
    queue = asyncio.Queue()
    for url in urls:
        await queue.put(url)
    
    sema = asyncio.BoundedSemaphore(value=100)
    async with aiohttp.ClientSession() as session:
        # 启动100个工作协程
        tasks = [asyncio.create_task(worker(session, queue, sema)) for _ in range(100)]
        await queue.join()  # 等待队列中所有任务处理完成
        for task in tasks:
            task.cancel()

if __name__ == '__main__':
    asyncio.run(main())

内容的提问来源于stack exchange,提问作者FunztNisch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:10:36