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

异步调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:41:28