我正在为我的REST服务编写API库。在某些时候,需要更新访问令牌。我试图实现一种线程安全的方法,因此只发送一个续订请求,即使多个线程可能想要同时续订它。
以下是我的代码的简化版本:
import requests
class Api():
def _renew(self):
# send a request which renews token in self._headers
def do_something(self, url, params=None):
r = requests(url, params=params, headers=self._headers)
if r.status_code == 401 and r.json()['error'] == 'Token expired':
# renew the access token
self._renew()
# repeat request with updated headers
r = requests(url, params=params, headers=self._headers)
return r.json()
我需要知道当前的续订请求是否正在进行中。我的想法是写下这样的续订功能:
def _renew(self):
lock.acquire()
# i want to check here if the lock.acquire() call blocked
# the thread and return immediately if it has
try:
# send a request to renew token header in self._headers
finally:
lock.release()
我希望其他线程可以调用do_something()
(以及随后的_renew()
)方法,直到第一个真正更新令牌并让其他人使用它的结果。
如何判断我的lock.acquire()
来电是否阻止?
在调用lock.locked()
之前检查acquire()
的结果不是线程安全的,有时多个线程会向服务器发送续订请求。
答案 0 :(得分:2)
您可以调用lock.acquire(False)
进行非阻塞调用,并使用返回值来确定是否已获取锁定。这看起来像这样:
def _renew(self):
# calling when the lock is already acquired
# will not block and return False
if not lock.acquire(False):
event.wait()
return
# else we acquired the lock
event.clear()
try:
# send a request to renew token header in self._headers
finally:
event.set()
lock.release()
请参阅the threading-docs了解python。
另一种方法是将令牌包装在另一个类中:
from threading import Event, RLock
class Token:
_internal = RLock()
_marker = False
def __init__(self):
# TODO set header
self._header = None
def _renew(self):
# TODO refresh the header
pass
def get(self):
with self._internal:
if self._marker:
self._renew()
self._marker = False
return self._header
# Marks the header to be refreshed at the next get()
def set_renew(self):
with self._internal:
self._marker = True
这有几个优点。首先,令牌对自己负责。在最好的环境中,它只会在需要时自行刷新,而不会被其他类刷新。这应该在Token#get(self)
中决定。这也通过将所有get-calls包装到一个锁中来解决线程安全问题,从而防止不必要的修改。