线程池执行时间异常:API轮询计时与线程使用问题排查
问题分析与解决
核心结论
计时方式没问题,当前日志现象的核心原因是API请求本身的响应延迟,而非线程池或计时逻辑错误;但你的线程池使用存在潜在风险。
具体解释
为什么Attempt都是0?
你的代码只有在第一次请求返回200时才会输出完成日志,而所有任务的Attempt都是0,说明每个任务的第一次API请求就成功返回200了。耗时的来源是API请求本身的响应时间,不是你预想的“多次重试+sleep”——比如耗时16秒的任务,是第一次请求就花了16秒才拿到响应,根本没进入sleep分支。为什么耗时逐渐增加?
这是API服务器的并发负载导致的:你同时发起8个请求,服务器处理第一个请求时负载低,响应快;后续请求因为服务器同时处理多个任务,资源占用升高,响应时间自然变长,所以耗时依次递增。线程池的潜在问题
你的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()代码优化建议
- 给
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
相关产品推荐
相关产品推荐

