Python:有没有线程安全的方法来知道lock.acquire()是否已阻止(并继续阻塞)?

时间:2014-12-10 12:37:55

标签: python multithreading python-3.x locking

我正在为我的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()的结果不是线程安全的,有时多个线程会向服务器发送续订请求。

1 个答案:

答案 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包装到一个锁中来解决线程安全问题,从而防止不必要的修改。