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

如何在asyncio中限制协程并发数量?解决脚本高并发卡顿问题

如何在asyncio中限制协程并发数量

当然可以实现这个需求!在asyncio生态里,控制协程并发数最常用也最简洁的方案就是使用asyncio.Semaphore(信号量)——它就像一个“并发闸门”,能严格限制同时运行的协程数量,达到上限后新的协程会自动等待,直到有已完成的协程释放闸门名额。

你的问题根源

你原来的代码直接用asyncio.gather(*p)一次性启动了12000个协程,虽然协程本身轻量,但大量协程同时进入调度队列会导致事件循环频繁切换上下文,消耗过多CPU资源,最终引发卡顿。用信号量就能完美解决这个问题。

修改后的完整代码

import asyncio

async def my_func(player, semaphore):
    # 用async with自动获取/释放信号量,无需手动管理
    async with semaphore:
        # 这里替换成你原来的await逻辑,比如调用异步API、处理IO等
        # await some_async_task(player)
        return f"Finished processing player: {player}"

async def main():
    # 设置并发上限为200
    concurrency_limit = 200
    semaphore = asyncio.Semaphore(concurrency_limit)
    
    # 为每个player创建任务,所有任务都会受信号量限制
    tasks = [my_func(player, semaphore) for player in players]
    
    # 等待所有任务完成,收集结果
    results = await asyncio.gather(*tasks)
    return results

if __name__ == "__main__":
    # 示例:模拟12000个player元素
    players = list(range(12000))
    
    # Python 3.7+推荐用asyncio.run,更简洁
    # result = asyncio.run(main())
    
    # 兼容旧版本Python的写法
    loop = asyncio.get_event_loop()
    result = loop.run_until_complete(main())

关键逻辑解释

  1. 信号量初始化:asyncio.Semaphore(200)创建了一个允许同时200个协程进入的“闸门”。
  2. 自动管理信号量:async with semaphore会在协程执行核心逻辑前自动获取信号量名额,执行完毕后自动释放——不用手动调用acquire()和release(),避免了遗漏释放导致的死锁问题。
  3. 任务调度:虽然我们创建了12000个任务,但每个任务都会卡在async with semaphore这一步,直到有已完成的协程释放名额,才会继续执行。这样就能严格保持同时运行的协程数不超过200。

额外提示

如果你的my_func里包含CPU密集型操作(而非纯IO操作),仅用信号量可能还不够——因为asyncio是单线程的,CPU密集型任务会阻塞事件循环。这种情况下可以结合loop.run_in_executor()把CPU任务放到线程池/进程池里执行,进一步优化性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:35:33