Wanneer en hoe u Python’s RLOCK

Lees door de Python-documenten die ik tegenkwam RLock.

Kan iemand me uitleggen (met voorbeeld) een scenario waarin RLockverdient de voorkeur aan Lock?

Met specifieke verwijzing naar:

  • RLock‘S “Recursion Level”. Hoe is dit nuttig?
  • een threads “eigendom” van een RLockobject
  • prestaties?

Antwoord 1, Autoriteit 100%

Dit is een voorbeeld waar ik het gebruik zie:

handig wanneer

  1. U wilt draadveilige toegang hebben van buiten de klas en gebruik dezelfde methoden van binnenin de klasse:

    class X:
        def __init__(self):
            self.a = 1
            self.b = 2
            self.lock = threading.RLock()
        def changeA(self):
            with self.lock:
                self.a = self.a + 1
        def changeB(self):
            with self.lock:
                self.b = self.b + self.a
        def changeAandB(self):
            # you can use chanceA and changeB thread-safe!
            with self.lock:
                self.changeA() # a usual lock would block at here
                self.changeB()
    
  2. voor recursie meer voor de hand liggend:

    lock = threading.RLock()
    def a(...):
         with lock:
             a(...) # somewhere inside
    

    Andere threads moeten wachten tot de eerste oproep van aAFWERKEN = Draadbezit.

prestaties

Meestal begin ik met het vergrendeling en wanneer de zaak 1 of 2 optreden, schakel ik naar een rlock. tot python 3.2 de rlock moet een beetje langzamer zijn vanwege de extra code. Het gebruikt slot:

Lock = _allocate_lock # line 98 threading.py
def RLock(*args, **kwargs):
    return _RLock(*args, **kwargs)
class _RLock(_Verbose):
    def __init__(self, verbose=None):
        _Verbose.__init__(self, verbose)
        self.__block = _allocate_lock()

Discussie-eigendom

Binnen de gegeven thread kunt u een RLockzo vaak verkrijgen als u wilt. Andere threads moeten wachten tot deze thread de resource opnieuw laat.

Dit is anders dan de Lockdie ‘Function-Call Ownership’ impliceert (ik zou het op deze manier noemen): een andere functie-oproep moet wachten totdat de bron zelfs wordt vrijgegeven door de laatste blokkerende functie Als het in dezelfde thread is = zelfs als het door de andere functie wordt gebeld.

wanneer u vergrendeling gebruikt in plaats van rlock

Wanneer u belt naar de buitenkant van de resource die u niet kunt besturen.

De onderstaande code heeft twee variabelen: A en B en de RLOCK moet worden gebruikt om ervoor te zorgen dat A == B * 2

import threading
a = 0 
b = 0
lock = threading.RLock()
def changeAandB(): 
    # this function works with an RLock and Lock
    with lock:
        global a, b
        a += 1
        b += 2
        return a, b
def changeAandB2(callback):
    # this function can return wrong results with RLock and can block with Lock
    with lock:
        global a, b
        a += 1
        callback() # this callback gets a wrong value when calling changeAandB2
        b += 2
        return a, b

In changeAandB2Het slot zou de juiste keuze zijn, hoewel het blokkeert. Of men kan het verbeteren met fouten met RLock._is_owned(). Functies zoals changeAandB2kan optreden wanneer u een Observer-patroon of een uitgever-abonnee hebt geïmplementeerd en daarna vergrendeling wilt toevoegen.


Antwoord 2, Autoriteit 8%

Hier is nog een gebruiksvoorbeeld voor RLock. Stel dat u een webgerichte gebruikersinterface hebt die gelijktijdige toegang ondersteunt, maar dat u bepaalde soorten toegang tot een externe bron moet beheren. U moet bijvoorbeeld consistentie handhaven tussen objecten in het geheugen en objecten in een database, en u hebt een managerklasse die de toegang tot de database regelt, met methoden die u moet garanderen dat ze in een specifieke volgorde worden aangeroepen, en nooit gelijktijdig.

Wat je kunt doen is een RLock en een Guardian-thread maken die de toegang tot de RLock regelt door het constant te verwerven en alleen los te laten wanneer er een signaal voor wordt gegeven. Vervolgens zorgt u ervoor dat alle methoden die u nodig hebt om de toegang te controleren, worden uitgevoerd om de vergrendeling te verkrijgen voordat ze worden uitgevoerd. Zoiets als dit:

def guardian_func():
    while True:
        WebFacingInterface.guardian_allow_access.clear()
        ResourceManager.resource_lock.acquire()
        WebFacingInterface.guardian_allow_access.wait()
        ResourceManager.resource_lock.release()
class WebFacingInterface(object):
    guardian_allow_access = Event()
    resource_guardian = Thread(None, guardian_func, 'Guardian', [])
    resource_manager = ResourceManager()
    @classmethod
    def resource_modifying_method(cls):
        cls.guardian_allow_access.set()
        cls.resource_manager.resource_lock.acquire()
        cls.resource_manager.update_this()
        cls.resource_manager.update_that()
        cls.resource_manager.resource_lock.release()
class ResourceManager(object):
    resource_lock = RLock()
    def update_this(self):
        if self.resource_lock.acquire(False):
            try:
                pass # do something
                return True
            finally:
                self.resource_lock.release()
        else:
            return False
    def update_that(self):
        if self.resource_lock.acquire(False):
            try:
                pass # do something else
                return True
            finally:
                self.resource_lock.release()
        else:
            return False

Op deze manier bent u verzekerd van de volgende zaken:

  1. Zodra een thread de resource lock verwerft, kan deze de beschermde methoden van de resource manager vrijelijk aanroepen, omdat RLock recursief is
  2. Zodra de thread de bronvergrendeling verkrijgt via de mastermethode in de webinterface, wordt alle toegang tot beveiligde methoden in de manager geblokkeerd voor andere threads
  3. Beschermde methoden in de manager zijn alleen toegankelijk door eerst een beroep te doen op de voogd.

Antwoord 3, autoriteit 6%

  • recursieniveau
  • eigendom

Een primitieve vergrendeling (Lock) is een synchronisatieprimitief die geen eigendom is van een bepaalde thread wanneer deze is vergrendeld.

Voor het herhaalbare slot (RLock) In de vergrendelde toestand is een thread eigenaar van het slot; in de ontgrendelde staat is geen enkele thread de eigenaar ervan.
Wanneer aangeroepen als deze thread al eigenaar is van het slot, verhoogt u het recursieniveau met één en keert u onmiddellijk terug. als thread het slot niet bezit, wacht het totdat de eigenaar het slot vrijgeeft.
Laat een vergrendeling los en verlaag het recursieniveau. Als het na de verlaging nul is, reset het slot dan naar ontgrendeld.

  • Prestaties

Ik denk niet dat er een prestatieverschil is, eerder een conceptueel verschil.

Other episodes