异步调用API时Asyncio Lock失效问题求助
问题分析与修复
你的代码核心问题是竞态条件:多个并发的fetch_data任务会同时通过lock.locked()判断,然后都读取同一个api_keys[0](此时无效密钥还没被移除),导致用同一个无效key重复请求API,连续返回401。而且lock.locked()的判断是非原子操作,根本起不到保护共享资源的作用。
修复后的代码
import asyncio import os import aiohttp api_keys = [] # 初始化密钥列表 lock = asyncio.Lock() async def fetch_data(session, url, params, delay): await asyncio.sleep(delay) global api_keys global lock while True: # 加锁获取当前要使用的API密钥,保证原子性 async with lock: if not api_keys: print("No more API keys available") return None current_key = api_keys[0] # 复制请求参数,避免并发任务互相干扰 req_params = params.copy() req_params["api_key"] = current_key os.environ["API_KEY"] = current_key async with session.get(url, params=req_params) as response: if response.status == 200: data = await response.json() print("Remaining requests", response.headers["x-requests-remaining"]) print("Used requests", response.headers["x-requests-used"]) return data else: print( f"Failed to get odds: status_code {response.status}, response body {await response.text()}".rstrip() ) if response.status == 401: # 加锁移除无效密钥,确保只有一个任务修改列表 async with lock: # 二次校验,防止其他任务已经移除了当前无效密钥 if api_keys and api_keys[0] == current_key: if len(api_keys) > 1: print("Using next API key in list") api_keys.pop(0) else: print("No more API keys available") return None # 回到循环开头,获取新密钥重试 continue # 非401错误直接返回 return None
关键修改说明
- 删掉了没用的
lock.locked()判断:这个判断和后续读取密钥的操作不是原子的,完全无法阻止多个任务同时拿到同一个无效密钥。 - 读取密钥时加锁:确保同一时间只有一个任务能获取当前的
api_keys[0],从根源避免竞态问题。 - 复制请求参数:原代码直接修改传入的
params,多个并发任务会互相覆盖参数,复制一份就能避免这个问题。 - 移除密钥时二次校验:防止其他任务已经把当前无效密钥移除,避免重复执行
pop(0)导致错误。 - 调整循环逻辑:处理完401后回到循环开头,重新获取新的密钥再发起请求,确保每次用的都是最新的有效密钥。
内容的提问来源于stack exchange,提问作者nzbroo
相关产品推荐
相关产品推荐

