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

线程池执行时间异常:API轮询计时与线程使用问题排查

问题分析与解决

核心结论

计时方式没问题,当前日志现象的核心原因是API请求本身的响应延迟,而非线程池或计时逻辑错误;但你的线程池使用存在潜在风险。

具体解释

  1. 为什么Attempt都是0?
    你的代码只有在第一次请求返回200时才会输出完成日志,而所有任务的Attempt都是0,说明每个任务的第一次API请求就成功返回200了。耗时的来源是API请求本身的响应时间,不是你预想的“多次重试+sleep”——比如耗时16秒的任务,是第一次请求就花了16秒才拿到响应,根本没进入sleep分支。

  2. 为什么耗时逐渐增加?
    这是API服务器的并发负载导致的:你同时发起8个请求,服务器处理第一个请求时负载低,响应快;后续请求因为服务器同时处理多个任务,资源占用升高,响应时间自然变长,所以耗时依次递增。

  3. 线程池的潜在问题
    你的main函数提交任务后直接结束,主线程退出时会强制终止ThreadPoolExecutor中所有未完成的线程。如果你的文件数量超过8个,后面的任务可能还没开始就被终止。正确的做法是等待所有任务完成:

    def main():
        # 用with语句自动管理线程池生命周期,会等待所有任务完成
        with ThreadPoolExecutor(max_workers=8) as pool:
            for filename in os.listdir(data_dir):
                pool.submit(poll_status, filename)
    

    或者手动等待任务完成:

    from concurrent.futures import as_completed
    
    def main():
        pool = ThreadPoolExecutor(max_workers=8)
        # 保存所有提交的任务对象
        futures = [pool.submit(poll_status, filename) for filename in os.listdir(data_dir)]
        # 等待所有任务完成
        for future in as_completed(futures):
            # 可在这里处理任务结果或异常
            pass
        pool.shutdown()
    
  4. 代码优化建议

    • 给requests.get添加超时时间,避免请求无限期挂起:
      resp = requests.get(f"https://api/{filename}/statuses", timeout=10)
      
    • 捕获网络异常,避免线程静默崩溃:
      try:
          resp = requests.get(f"https://api/{filename}/statuses", timeout=10)
          resp.raise_for_status()  # 捕获HTTP错误状态码(如500)
      except requests.exceptions.RequestException as e:
          logging.error(f"请求{filename}失败: {str(e)}")
          time.sleep(3)
          continue
      
    • 处理循环结束仍未成功的情况,添加日志提示:
      def poll_status(filename: str):
          start_time = perf_counter()
          success = False
          for i in range(30):
              logging.info(f"Attempt {i} for {filename}")
              try:
                  resp = requests.get(f"https://api/{filename}/statuses", timeout=10)
                  if resp.status_code == 404:
                      time.sleep(3)
                  elif resp.status_code == 200:
                      end_time = perf_counter()
                      logging.info(f'{filename} took {end_time - start_time: 0.2f} seconds to complete. (Attempt {i})')
                      success = True
                      break
              except Exception as e:
                  logging.error(f"Attempt {i} for {filename} failed: {str(e)}")
                  time.sleep(3)
          if not success:
              logging.error(f'{filename} failed after 30 attempts.')
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 00:35:16