讲师中心 微信公众号

工具推荐

视频效率加速
Seko
Seko

AI短剧一键生成成片

updream

一键生成高质量推广成片

Laper
Laper

剧本策划到成片协作

AniShort
AniShort

AI短剧协同制作平台

高效控制线程池批量拉取 API 分块数据(动态终止无用任务)

小雪同学_6598

小雪同学_6598

发布时间:2026-02-20 10:49:00

|

575人浏览过

|

来源于php中文网

原创

高效控制线程池批量拉取 API 分块数据(动态终止无用任务)

本文介绍如何避免为无效数据块创建冗余线程,通过事件驱动 + 批量提交 + 提前终止机制,在调用分页/分块 api 时实现线程资源的精准调度与高效利用。

本文介绍如何避免为无效数据块创建冗余线程,通过事件驱动 + 批量提交 + 提前终止机制,在调用分页/分块 api 时实现线程资源的精准调度与高效利用。

在并行调用分块 API(如分页拉取、分片查询)时,一个常见但易被忽视的性能陷阱是:盲目提交全部预估块数的任务。例如,预设 maxBlocks = 1000,却实际仅存在前 39 个有效块(第 40 块起返回 None),此时若一次性提交 1000 个线程任务,将造成大量空转、资源浪费与调度开销。

原代码的问题在于:

  • 使用集合推导式 {executor.submit(...), i for i in range(maxBlocks)} 一次性提交全部任务;
  • 缺乏对“终止信号”的感知,无法在首次遇到 None 后及时中止后续提交;
  • as_completed 仅用于结果收集,不参与流程控制,导致无效任务持续运行。

✅ 正确解法的核心思想是:按批提交 + 实时终止 + 资源节制。我们引入 threading.Event 作为全局“结束信号”,由任意 worker 在检测到 None 时主动触发;主线程在每批提交后检查该信号,并在必要时暂停或退出循环。

以下为优化后的完整实现:

import logging
import random
import time
from concurrent.futures import ThreadPoolExecutor
from threading import Event

logging.basicConfig(
    level=logging.DEBUG,
    format="%(levelname)-8s | %(funcName)-18s | %(message)s",
)

# 模拟真实场景:API 实际只返回前 N 个有效块(N ∈ [10, 30])
SIMULATED_BLOCKS_COUNT = random.randint(10, 30)
MAX_BLOCKS = 1000  # 上限预估(保守值,非实际需求数)

def fetch_block(step: int, done_event: Event) -> list | None:
    """模拟带终止感知的 API 请求函数"""
    time.sleep(random.uniform(0.1, 0.5))  # 模拟网络延迟

    # ✅ 关键逻辑:一旦 step 超出真实数据边界,标记结束并返回 None
    if step >= SIMULATED_BLOCKS_COUNT:
        logging.debug("step=%d → No more data, signaling termination", step)
        done_event.set()  # 全局通知:停止提交新任务
        return None

    # 返回模拟数据块(实际中为 JSON 列表等)
    return [f"item_{step}_1", f"item_{step}_2"]

def parallel_fetch_blocks(
    max_workers: int = 10,
    batch_size: int = 10,
    timeout_per_batch: float = 3.0
) -> list:
    """
    并行拉取所有有效数据块,自动终止无效任务提交

    Args:
        max_workers: 线程池最大并发数
        batch_size: 每批提交的任务数(控制粒度与浪费平衡)
        timeout_per_batch: 每批提交后等待终止信号的最大时长(秒)

    Returns:
        扁平化后的全部有效数据列表
    """
    done_event = Event()
    futures = {}  # {step: Future}

    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        for step in range(MAX_BLOCKS):
            # ? 全局终止检查:一旦收到信号,立即退出循环
            if done_event.is_set():
                logging.info("Termination signal received at step=%d, stopping submission", step)
                break

            # ? 批次控制:每 batch_size 步暂停一次,等待已提交批次反馈
            if step > 0 and step % batch_size == 0:
                logging.debug("step=%d → entering batch pause (timeout=%.1fs)", step, timeout_per_batch)
                # 等待终止信号 —— 若超时未触发,则继续;若触发则立刻退出
                if done_event.wait(timeout=timeout_per_batch):
                    logging.debug("Termination signal caught during batch pause")
                    break

            # ? 提交当前 step 任务
            future = executor.submit(fetch_block, step, done_event)
            futures[step] = future

    # ✅ 安全收集结果:只取连续有效的前 N 个块(以首个 None 为界)
    blocks_data = []
    for step in range(len(futures)):
        result = futures[step].result()
        if result is None:
            logging.info("First None encountered at step=%d → total valid blocks: %d", step, step)
            break
        blocks_data.extend(result)  # 注意:此处假设返回可迭代对象

    return blocks_data

# 使用示例
if __name__ == "__main__":
    logging.info("Starting parallel block fetch...")
    all_data = parallel_fetch_blocks(
        max_workers=8,
        batch_size=5,
        timeout_per_batch=2.0
    )
    logging.info("✅ Fetched %d items across %d valid blocks", len(all_data), len(all_data) // 2)

⚠️ 关键注意事项

  • batch_size 是性能与资源的权衡点:

    • 过小(如 1)→ 频繁检查 done_event,降低吞吐;
    • 过大(如 100)→ 可能多创建最多 batch_size−1 个无效任务(如真实块数为 24,batch_size=10 时最多浪费 6 个)。推荐从 5~20 开始测试,结合 API 响应方差调整。
  • timeout_per_batch 不宜过长:
    若设置为 30s,而首个 None 出现在第 25 步,线程池可能在 step=30 才响应终止,造成额外延迟。建议设为略大于单次请求 P95 延迟(如 2–5s)。

  • 结果收集必须按序截断:
    因 as_completed 不保证顺序,且 None 可能在任意时刻返回,故不可依赖 future.result() 的遍历顺序。本方案采用 for step in range(len(futures)) 严格按提交序号检查,确保前缀连续性。

  • 异常处理增强建议(生产环境必备):
    在 fetch_block 中应包裹 try/except,对网络错误、解析失败等返回 None 或重试策略,并考虑使用 concurrent.futures.wait(..., return_when=FIRST_EXCEPTION) 主动捕获异常流。

通过该模式,你不仅能将线程浪费降至最低(理论最大浪费 ≤ batch_size − 1),还能显著提升高延迟 API 场景下的响应灵敏度——真正实现「有数据才干活,没数据即停手」的智能并发调度。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4818

2023.08.10

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

0

2026.11.12

Go-Zero安装与goctl环境配置教程
Go-Zero安装与goctl环境配置教程

本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。

0

2026.11.12

Go-Zero零基础入门教程
Go-Zero零基础入门教程

本专题整理Go-Zero的入门知识,从框架定位、环境准备到跑通第一个API服务,讲清目录分层、配置加载、日志与中间件的基本用法,并对比Gin、Kratos的差异,帮助Go开发者建立整体认知,判断它是否适合当前项目。

0

2026.11.12

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

120

2026.11.06

Postman请求头Headers怎么设置
Postman请求头Headers怎么设置

围绕Postman HTTP请求头配置展开,讲解Headers选项的具体使用方式,包括Key和Value参数填写、请求头信息添加、常见Header字段设置及请求头修改方法,帮助用户解决接口调试过程中遇到的请求头配置问题。

60

2026.11.06

Postman接口请求发送教程
Postman接口请求发送教程

详细介绍Postman接口请求的创建与发送方法,涵盖GET和POST请求、URL配置、请求参数、Headers请求头、Body请求体设置以及响应结果查看,帮助用户掌握Postman接口请求的完整操作流程。

80

2026.11.06

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

80

2026.11.05

DBeaver安全防护教程大全
DBeaver安全防护教程大全

DBeaver安全防护专题,围绕数据库连接安全、账号密码保护和访问权限管理展开,整理SSL/TLS加密连接、SSH隧道、数据库凭据保存及证书验证等配置方法,同时介绍敏感信息保护、远程连接风险和高权限账号使用等注意事项,帮助用户提高数据库日常管理的安全性。

60

2026.11.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn