API分块数据并行获取优化:避免创建不必要线程
动态控制任务提交,避免无效API请求
先唠唠你原代码的问题:一次性提交了所有maxBlocks的请求,哪怕某个块返回None,后面更大序号的请求已经发出去了,完全是浪费资源。要实现「检测到None后不再创建更大序号的任务」,得动态提交任务,不能一股脑把所有任务都扔给线程池。
优化思路
- 不预先提交所有任务,先提交一批(数量和线程池大小一致)
- 处理每个完成的请求时,跟踪当前的块序号
- 一旦某个块返回
None,立刻停手,不再提交更大序号的任务,甚至可以取消已经提交但还没跑完的、序号更大的任务 - 没碰到
None的话,就继续提交下一个序号的任务,直到把所有有效块都捞完
修正后的代码
def func(step): # 这里放你原有的API获取逻辑 block = ... # 从API拿对应step的块数据 if len(block) == 0: return None else: return block def fetch_all_blocks(maxBlocks): blocks_data = [] stop_fetching = False next_step = 0 max_workers = 10 with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: # 先提交第一批任务,数量和线程池一致 futures = {} while next_step < min(maxBlocks, max_workers): future = executor.submit(func, next_step) futures[future] = next_step next_step += 1 while futures: # 等任意一个任务跑完 done, _ = concurrent.futures.wait(futures, return_when=concurrent.futures.FIRST_COMPLETED) for future in done: step = futures.pop(future) result = future.result() if result is not None: blocks_data.extend(result) # 还没到最大块数、也没触发停止的话,继续提交下一个任务 if not stop_fetching and next_step < maxBlocks: next_future = executor.submit(func, next_step) futures[next_future] = next_step next_step += 1 else: # 碰到None了,标记停止,不再提交后续任务 stop_fetching = True # 可选:取消已经提交但还没跑完的、序号比当前大的任务,进一步省资源 for pending_future, pending_step in list(futures.items()): if pending_step > step: pending_future.cancel() # 清空剩余任务,提前结束循环 futures.clear() break return blocks_data
代码说明
- 动态提交:一开始只提交和线程池大小相同的任务,避免资源瞬间占满,后续按需提交,更灵活
- 停止机制:一旦某个块返回
None,立刻打住,不再提交新任务,还能取消已提交的无效任务(这个是可选操作,看你要不要省这点资源) - 数据收集:用
extend代替+=,处理列表时更高效 - 修正语法错误:你原代码里的
futures = {executor.submit(func, step), i for i in range(maxBlocks)}是错的,这里用字典把每个future和对应的step绑定,方便跟踪是哪个块的请求
额外小技巧
- 如果API允许,可以先请求一个偏后的块(比如
maxBlocks的一半),快速找到边界,减少要提交的任务数量 - 可以加个重试逻辑,避免因为网络波动导致某个块临时返回
None,误判成没有数据
内容的提问来源于stack exchange,提问作者Martí Castell
相关产品推荐
相关产品推荐

