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

百万网页下载:固定协程工作者VS单URL协程+asyncio.Semaphore?

百万网页下载:固定协程工作者 vs Semaphore限制协程

针对百万级URL的下载需求,两种方案的差异和选择建议如下:

方案一:固定协程工作者 + 任务队列

核心逻辑

创建固定数量的worker协程(示例中为10个),先将所有URL放入异步队列,worker持续从队列中取出URL执行下载,直到队列空了才退出。

async def worker(queue):
    while True:
        try:
            url = queue.get_nowait()
        except QueueEmpty:
            break
        await download(url)

async def main():
    queue = asyncio.Queue()
    for url in urls:
        await queue.put(url)
    workers = [worker(queue) for _ in range(10)]
    await asyncio.wait(workers)

asyncio.run(main())

优势

  • 内存占用极低:仅固定数量的协程处于活跃状态,百万URL仅以字符串形式存储在队列中,内存消耗完全可控,不会因协程对象过多导致内存溢出。
  • 调度开销小:事件循环只需管理少量活跃协程,调度压力低,整体运行更稳定。
  • 扩展性强:即使任务量翻倍,只需调整worker数量即可适配,无需改动核心逻辑。

不足

需要手动维护任务队列,代码多了队列初始化、入队的步骤,逻辑稍显繁琐。


方案二:Semaphore + 每个URL一个协程

核心逻辑

为每个URL单独创建一个协程,用asyncio.Semaphore限制同时执行的协程数量(示例中为10个),超出数量的协程会等待信号量释放后再执行。

async def worker(url, sema):
    async with sema:
        await download(url)

async def main():
    semaphore = asyncio.Semaphore(10)
    workers = [worker(url, semaphore) for url in urls]
    await asyncio.wait(workers)

asyncio.run(main())

优势

  • 代码简洁直观:无需维护队列,直接为每个任务生成协程,用信号量控制并发,逻辑一目了然。
  • 问题定位方便:每个协程对应一个URL,单个任务出错时更容易追踪具体问题。

不足

  • 内存压力大:百万个协程对象(即使处于等待状态)会占用大量内存,可能导致内存资源紧张。
  • 调度成本高:事件循环需要管理百万个等待中的协程,调度开销会显著增加,影响整体执行效率。

最终选择建议

如果是百万级这种超大规模的下载任务,优先选择方案一——固定协程工作者+队列的模式,它能更好地控制内存和调度开销,保证任务稳定运行。

如果任务量较小(比如几万级URL),方案二的简洁性更有优势,开发成本更低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:37:48