Lees door de Python-documenten die ik tegenkwam RLock
.
Kan iemand me uitleggen (met voorbeeld) een scenario waarin RLock
verdient de voorkeur aan Lock
?
Met specifieke verwijzing naar:
RLock
‘S “Recursion Level”. Hoe is dit nuttig?- een threads “eigendom” van een
RLock
object - prestaties?
Antwoord 1, Autoriteit 100%
Dit is een voorbeeld waar ik het gebruik zie:
handig wanneer
-
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()
-
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
a
AFWERKEN = 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 RLock
zo vaak verkrijgen als u wilt. Andere threads moeten wachten tot deze thread de resource opnieuw laat.
Dit is anders dan de Lock
die ‘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 changeAandB2
Het slot zou de juiste keuze zijn, hoewel het blokkeert. Of men kan het verbeteren met fouten met RLock._is_owned()
. Functies zoals changeAandB2
kan 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:
- Zodra een thread de resource lock verwerft, kan deze de beschermde methoden van de resource manager vrijelijk aanroepen, omdat RLock recursief is
- 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
- 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.