Как управлять доступом к функции на основе последнего времени доступа с помощью потоков?

#python #python-3.x #multithreading

#python #python-3.x #многопоточность

Вопрос:

Я хочу контролировать доступ к функции на основе времени, прошедшего с момента последнего доступа.

Для этой цели я реализовал следующий декоратор, который отлично работает при выполнении одного потока:

 import time

MIN_ELAPSED_TIME = 0.4
time_last_get = time.time()

def timingRequests(f):
    def wrapper(*args, **kwargs):
        global time_last_get  
        elapsed_time_since_last_get = time.time() - time_last_get
        if elapsed_time_last_request < MIN_REQUEST_TIME: # not enough time has passed since the last request
          time.sleep(MIN_REQUEST_TIME - elapsed_time_last_request)

        # At this point, at least MIN_ELAPSED_TIME has passed since last get
        time_last_get = time.time()
        r = f(*args, **kwargs)
        return r
    return wrapper

@timingRequests
doGet()
 

Я хочу, чтобы n потоков (на данный момент 2) обращались к этой функции И учитывали time_last_get между потоками. Моя проблема в том, что каждый поток ожидает выполнения doGet() на основе своего предыдущего выполнения doGet()

Я пытался использовать многопоточность.Событие() как семафор, потоковая передача.Lock() но все равно оба потока обращаются к функции почти одновременно.

Поведение при использовании 2 или более потоков заключается в том, что каждый поток ожидает выполнения doGet() на основе time_last_get этого же потока, вместо этого принимая во внимание доступ других потоков.

Поведение, которого я хочу достичь, является следующим:

Поток-1: doGet()

Поток-2: ожидает (при необходимости) секунды MIN_ELAPSED_TIME, прошедшие с момента обращения к потоку-2

Поток-2: doGet()

Поток-1: ожидает (при необходимости) секунды MIN_ELAPSED_TIME, прошедшие с момента обращения к потоку-2

Поток-1: doGet()

Поток-1: ожидает (при необходимости) секунды MIN_ELAPSED_TIME, прошедшие с момента обращения к потоку-1

Ответ №1:

Я понял, в чем проблема.

Во-первых, я обошел проблему с помощью многопоточности.Экземпляры Events():

 import time

MIN_ELAPSED_TIME = 0.4
time_last_get = time.time()
e = threading.Event()
e.set()

def timingRequests(f):
    def wrapper(*args, **kwargs):
        e.wait()
        e.clear()
        global time_last_get  
        elapsed_time_since_last_get = time.time() - time_last_get
        if elapsed_time_last_request < MIN_REQUEST_TIME: # not enough time has passed since the last request
          time.sleep(MIN_REQUEST_TIME - elapsed_time_last_request)

        # At this point, at least MIN_ELAPSED_TIME has passed since last get
        time_last_get = time.time()
        r = f(*args, **kwargs)
        e.set()
        return r
    return wrapper

@timingRequests
doGet()
 

Но я все еще пытался понять, почему это решение работает, но не использует блокировку.

Ну, проблема была в этом:

 def timingRequests(f):
    def wrapper(*args, **kwargs):
        with threading.Lock():
            # Code
 

Каждый поток, достигающий этой части кода, создавал (разблокированный) экземпляр threading .Lock() , поэтому блокировка работала не так, как ожидалось. Тогда решение состоит в том, чтобы создать блокировку вне функции timingRequests и использовать ее таким образом:

 lock = threading.Lock()

def timingRequests(f):
    def wrapper(*args, **kwargs):
        with lock:
            # Code