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

同一函数内ContextVar的set与reset失败:上下文不一致问题求助

问题:ContextVar重置时出现跨上下文错误

我编写了如下函数:

async_session = contextvars.ContextVar("async_session")

async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        try:
            _token = async_session.set(session)
            yield session
        finally:
            async_session.reset(_token)

运行时出现如下错误:

ValueError: <Token var=<ContextVar name='async_session' at 0x7e1470e00e00> at 0x7e14706d4680> was created in a different Context

我对此感到困惑:据我所知,Context的唯一变更方式是触发完整函数调用,那yield期间上下文为何会发生变化?该函数被用作FastAPI的Depends依赖项,运行环境为Python 3.8及旧版FastAPI 0.54。


解决方案与分析

问题根源

在Python 3.8搭配旧版FastAPI(0.54)的环境中,FastAPI对生成器类型依赖的调度存在缺陷:请求处理过程中如果发生异步任务切换(比如异步IO操作挂起后被调度到其他事件循环线程),会导致yield前后的上下文环境不一致。而ContextVar的reset操作严格要求必须在创建Token的同一个上下文中执行,这就触发了跨上下文的报错。

修复方案

完全不需要手动调用async_session.reset(_token),Python的ContextVar本身就具备上下文自动恢复的特性。修改后的代码如下:

async_session = contextvars.ContextVar("async_session")

async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        _token = async_session.set(session)
        yield session
        # 无需手动reset,上下文退出时自动恢复原有值

如果保留try-finally结构,只需移除reset调用即可:

async_session = contextvars.ContextVar("async_session")

async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        try:
            _token = async_session.set(session)
            yield session
        finally:
            pass  # 移除reset调用

补充说明

Python 3.8及以上版本的ContextVar设计中,当你在某个上下文中调用set()获取到Token后,一旦该上下文结束(比如生成器执行完毕、异步任务完成),ContextVar会自动回滚到Token对应的旧值。旧版FastAPI的调度逻辑让finally块中的代码跑在了和set()不同的上下文,手动reset就触发了错误,去掉手动调用就能解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:55:01