如何在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())
关键逻辑解释
- 信号量初始化:
asyncio.Semaphore(200)创建了一个允许同时200个协程进入的“闸门”。 - 自动管理信号量:
async with semaphore会在协程执行核心逻辑前自动获取信号量名额,执行完毕后自动释放——不用手动调用acquire()和release(),避免了遗漏释放导致的死锁问题。 - 任务调度:虽然我们创建了12000个任务,但每个任务都会卡在
async with semaphore这一步,直到有已完成的协程释放名额,才会继续执行。这样就能严格保持同时运行的协程数不超过200。
额外提示
如果你的my_func里包含CPU密集型操作(而非纯IO操作),仅用信号量可能还不够——因为asyncio是单线程的,CPU密集型任务会阻塞事件循环。这种情况下可以结合loop.run_in_executor()把CPU任务放到线程池/进程池里执行,进一步优化性能。
内容的提问来源于stack exchange,提问作者Blaszard
相关产品推荐
相关产品推荐

