简体   繁体   English

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

[英]Python: Is there a thread safe way to know if lock.acquire() has blocked (and continue blocking)?

I'm writing an API library for my REST service. 我正在为我的REST服务编写API库。 At some point, access token will need to be renewed. 在某些时候,访问令牌将需要更新。 I'm trying to implement a thread-safe way to do it so only one renew request is sent, even though multiple threads may want to renew it at the same time. 我正在尝试实现一种线程安全的方法来执行此操作,因此即使多个线程可能希望同时更新它,也仅发送一个更新请求。

Here is a simplified version of my code: 这是我的代码的简化版本:

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()

I need to know if a current renew request is in progress. 我需要知道当前的续订请求是否正在进行。 My idea was to write the renew function like this: 我的想法是这样编写续订功能:

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()

I want other threads which may call do_something() (and subsequently _renew() ) method to wait until the first really renews the tokens and make others use it's result. 我希望其他线程可以调用do_something() (随后是_renew() )方法,直到第一个真正更新令牌并使其他人使用它的结果为止。

How can I tell if my lock.acquire() call was blocking or not? 我怎么知道我的lock.acquire()调用是否被阻止?

Checking the result of lock.locked() before calling acquire() is not thread-safe and sometimes more than one thread send renew request to the server. 在调用acquire() lock.locked()之前检查lock.locked()的结果不是线程安全的,有时有多个线程向服务器发送更新请求。

You can call lock.acquire(False) for a nonblocking call and use the return value to determine if a lock has been acquired. 您可以调用lock.acquire(False)进行无阻塞调用,并使用返回值确定是否已获取锁。 This would look like this: 看起来像这样:

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()

See the threading-docs for python. 请参阅python 的threading-docs

Another approach is to wrap the token in another class: 另一种方法是将令牌包装在另一个类中:

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

This has several advantages. 这具有几个优点。 First of all the token is responsible for itself. 首先,令牌由自己负责。 In the best possible environment it would only refresh itself whenever needed and NOT get refreshed by other classes. 在最佳环境中,它只会在需要时刷新自身,而不会被其他类刷新。 This should be decided in Token#get(self) . 这应该在Token#get(self) This also solves thread-safety by wrapping all get-calls into a lock, preventing unwanted modifications. 这也通过将所有get调用包装到锁中来解决线程安全问题,从而防止不必要的修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM