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

Python中asyncio.Condition未生效,关键API仍重复调用排查

排查asyncio.Condition缓存失效问题

以下是导致你遇到的「所有账号都触发API调用」问题的常见原因,以及对应的修正思路:

1. 未在锁保护下操作缓存

asyncio.Condition的核心是锁+条件通知,所有读写缓存字典的操作必须在async with cond:上下文内执行。如果修改缓存时没持有锁,其他协程可能看不到最新的缓存数据,导致条件判断失效。

比如错误写法:

# 错误:修改缓存时没加锁
cache[account_id] = data
cond.notify_all()

正确写法必须包裹在锁内:

async with cond:
    cache[account_id] = data
    cond.notify_all()

2. 等待条件前未先检查缓存状态

很多人会直接调用await cond.wait(),但正确逻辑应该是先检查缓存,不存在再等待。否则当缓存已经被其他协程填充后,当前协程还会无意义地等待通知,甚至错过缓存数据直接走API调用逻辑。

错误写法:

async with cond:
    await cond.wait()
    # 才检查缓存,此时可能已经错过更新
    if account_id not in cache:
        data = await call_api()
        cache[account_id] = data

正确写法(循环检查+等待,避免虚假唤醒):

async with cond:
    while account_id not in cache:
        await cond.wait()
    return cache[account_id]

3. 缓存更新后未触发通知

当PAYER账号获取到子账号数据并更新缓存后,必须调用cond.notify_all()(或cond.notify())唤醒所有等待的协程。如果漏掉这一步,其他子账号任务会一直阻塞在wait状态,直到超时(如果设置了),甚至直接跳过等待去调用API。

4. 任务逻辑未阻塞等待缓存,直接并行执行

如果你的代码里,协程只是简单检查缓存,不存在就直接调用API,没有用Condition的wait来阻塞,那么所有任务会同时启动,此时缓存为空,所有任务都会触发API调用。

修正后的核心逻辑示例

import asyncio

# 全局缓存字典
account_cache = {}
# 条件变量
cache_cond = asyncio.Condition()

async def get_account_data(account_id):
    async with cache_cond:
        # 先检查缓存,存在直接返回
        if account_id in account_cache:
            return account_cache[account_id]
        
        # 处理PAYER账号,获取所有子账号数据
        if account_id == "PAYER":
            # 模拟调用API获取 payer 及其子账号数据
            payer_info = await fetch_payer_api()
            # 更新缓存:存入payer本身
            account_cache[account_id] = payer_info["self_data"]
            # 存入所有子账号数据
            for sub_acc, sub_data in payer_info["sub_accounts"].items():
                account_cache[sub_acc] = sub_data
            # 唤醒所有等待的子账号任务
            cache_cond.notify_all()
            return payer_info["self_data"]
        else:
            # 子账号:循环等待缓存被填充
            while account_id not in account_cache:
                await cache_cond.wait()
            return account_cache[account_id]

async def fetch_payer_api():
    # 模拟API调用延迟
    await asyncio.sleep(1)
    return {
        "self_data": {"type": "payer", "balance": 1000},
        "sub_accounts": {
            "SUB001": {"type": "sub", "balance": 200},
            "SUB002": {"type": "sub", "balance": 300}
        }
    }

async def main():
    target_accounts = ["PAYER", "SUB001", "SUB002"]
    tasks = [get_account_data(acc) for acc in target_accounts]
    results = await asyncio.gather(*tasks)
    for acc_id, data in zip(target_accounts, results):
        print(f"账号 {acc_id} 数据: {data}")

asyncio.run(main())

这个示例中:

  • 所有缓存操作都在锁保护下执行,保证协程安全
  • 子账号任务会循环等待缓存,直到被PAYER任务唤醒
  • PAYER任务更新缓存后立即触发通知,避免其他任务重复调用API
  • 最终只有PAYER会调用API,子账号直接复用缓存数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:18:17