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

Python协程中请求头Token未实时更新的问题咨询

问题分析与解决方案

你的问题核心在于多线程环境下共享变量的内存可见性问题:你在独立线程的事件循环中更新self.token,但主线程(运行HTTP请求的事件循环)读取self.token时,可能读取的是CPU缓存中的旧值,而非主内存里的最新值。即使_get_headers()是在获取信号量后调用的,由于缺少同步机制,主线程无法感知其他线程对self.token的修改,导致请求头里的token始终保持初始值。

下面给出两种可行的解决方案,优先推荐第一种(更符合asyncio的设计理念):

方案一:合并事件循环,避免多线程

既然你用的是asyncio,完全不需要单独开线程来更新token——直接在同一个事件循环里后台运行token更新任务即可,这样既消除了多线程的同步问题,也更符合异步编程的范式。

修改后的完整代码:

import asyncio
import aiohttp
from random import randint

class SOME_CLASS(object):
    def __init__(self):
        self.token = None
        self.loop = asyncio.get_event_loop()
        self.semaphore = asyncio.Semaphore(3)
        self.session = aiohttp.ClientSession(loop=self.loop)
        # 将token更新作为后台异步任务运行
        self.token_update_task = asyncio.create_task(self.get_token())

    def _get_headers(self):
        headers = {
            'x-access-token': str(self.token),
            'content-type': 'application/json',
        }
        return headers

    async def get_token(self):
        while True:
            self.token = randint(1, 100)
            await asyncio.sleep(2)

    async def fetch(self, url):
        async with self.semaphore:
            headers = self._get_headers()
            async with self.session.get(url, headers=headers) as response:
                await response.read()
            return headers

    async def fetch_all(self):
        urls = [f'https://httpbin.org/get?x={i}' for i in range(1000)]
        futures = asyncio.as_completed([self.fetch(url) for url in urls])
        for future in futures:
            await asyncio.sleep(1)
            headers = await future
            print(f'headers: {headers} token: {self.token}')

    def run(self):
        try:
            self.loop.run_until_complete(self.fetch_all())
        finally:
            # 清理资源:关闭session并取消后台任务
            self.loop.run_until_complete(self.session.close())
            self.token_update_task.cancel()

if __name__ == '__main__':
    sc = SOME_CLASS()
    sc.run()

方案二:线程安全地访问token(若必须保留多线程)

如果你因为某些原因必须用单独线程运行token更新,需要用线程锁来保证self.token的读写同步,同时强制内存刷新,让主线程能看到最新的token值。

修改后的关键代码:

import asyncio
import aiohttp
from threading import Thread, Lock
from random import randint

LOOP = asyncio.get_event_loop()
NEW_LOOP = asyncio.new_event_loop()
t = Thread(target=NEW_LOOP.run_forever)
t.start()

class SOME_CLASS(object):
    def __init__(self, loop=LOOP, new_loop=NEW_LOOP):
        self._token = None
        self.token_lock = Lock()  # 线程锁,保证读写同步
        self.loop = loop
        self.new_loop = new_loop
        self.semaphore = asyncio.Semaphore(3)
        self.session = aiohttp.ClientSession(loop=self.loop)
        asyncio.run_coroutine_threadsafe(self.get_token(), self.new_loop)

    # 用属性包装token,读写时自动加锁
    @property
    def token(self):
        with self.token_lock:
            return self._token

    @token.setter
    def token(self, value):
        with self.token_lock:
            self._token = value

    def _get_headers(self):
        headers = {
            'x-access-token': str(self.token),
            'content-type': 'application/json',
        }
        return headers

    # 其余方法保持不变...

两种方案都能解决你的问题,方案一的代码更简洁,也更符合asyncio的设计初衷,优先推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:08:18