Python多线程API调用重复问题排查:ThreadPoolExecutor代码疑问
排查ThreadPoolExecutor调用API时重复请求的问题
先从你怀疑的get_session函数入手,结合多线程场景的常见问题,给你几个排查方向和错误示例:
1. 检查get_session的线程安全实现
如果你的get_session是全局共享Session对象,且存在重复绑定钩子/修改状态的逻辑,会导致请求被"重复触发"的假象,甚至真的重复发送请求。比如这种错误实现:
# 错误示例:全局Session+重复添加响应钩子 global_session = None def get_session(): global global_session if global_session is None: global_session = requests.Session() # 每次调用get_session都添加一次钩子,同一个请求会被多次触发日志 global_session.hooks['response'].append(lambda r: print(f"请求URL: {r.url}")) return global_session
这种情况下,每调用一次get_session就给Session加一个钩子,线程越多钩子越多,同一个请求的日志会被打印多次,看起来像是重复调用,但实际只发送了一次请求。
正确的线程隔离Session实现应该用threading.local,保证每个线程有独立的Session:
import threading import requests thread_local = threading.local() def get_session(): if not hasattr(thread_local, "session"): thread_local.session = requests.Session() return thread_local.session
2. 确认URL列表是否被重复生成
先验证你的URL列表本身是否存在重复,这是最容易忽略的点:
print(f"原始URL总数: {len(urls)}") unique_urls = set(urls) print(f"去重后URL总数: {len(unique_urls)}") if len(urls) != len(unique_urls): print("URL列表存在重复,请检查分页生成逻辑")
比如分页循环时错误嵌套循环、分页参数计算错误,都会导致同一个URL被多次加入列表。
3. 检查请求函数的重试逻辑
如果你的fetch_url函数内部有重试机制,且重试次数刚好是6次,也会出现每个URL被调用6次的情况:
# 错误示例:无差别重试6次 def fetch_url(url): session = get_session() for _ in range(6): try: resp = session.get(url) resp.raise_for_status() return resp.json() except Exception: # 没有判断重试条件,直接重试 pass
这种情况即使URL只提交一次,也会因重试发送6次请求。
4. 排查任务提交环节是否重复提交
检查你使用ThreadPoolExecutor的代码,是否不小心重复提交了同一批URL:
# 错误示例:重复调用map/submit with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor: executor.map(fetch_url, urls) executor.map(fetch_url, urls) # 重复提交,导致每个URL被处理两次
或者使用as_completed时,错误地重复遍历了任务列表。
快速定位的调试技巧
在请求函数开头加线程ID和URL的日志,能直接看出是任务重复提交还是函数内部重复调用:
import threading def fetch_url(url): print(f"线程ID: {threading.get_ident()} | 请求URL: {url}") session = get_session() # 后续请求逻辑
如果同一个URL出现多次且线程ID不同,说明任务被重复提交;如果线程ID相同,说明函数内部有重复请求逻辑。
内容的提问来源于stack exchange,提问作者Brian - RGY Studio
相关产品推荐
相关产品推荐

