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
相关产品推荐
相关产品推荐

