阅读我遇到的 Python 文档RLock
。
有人可以向我解释(举例)一个RLock
更喜欢的场景Lock
吗?
特别是:
RLock
的“递归级别”。这有什么用?RLock
对象的线程“所有权”- 表现?
这是我看到使用的一个例子:
有用的时候
您希望从类外部进行线程安全访问,并在类内部使用相同的方法:
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()
对于递归更明显:
lock = threading.RLock()
def a(...):
with lock:
a(...) # somewhere inside
其他线程必须等到第一次调用a
完成=线程所有权。
表现
通常,我从 Lock 开始编程,当情况 1 或 2 发生时,我切换到 RLock。在 Python 3.2 之前,由于额外的代码,RLock 应该会慢一些。它使用锁:
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()
线程所有权
在给定的线程中,您可以随心所欲地获取 a RLock
。其他线程需要等到该线程再次释放资源。
这与Lock
暗示“函数调用所有权”不同(我会这样称呼它):另一个函数调用必须等到最后一个阻塞函数释放资源,即使它在同一个线程中=即使它由另一个函数调用。
何时使用 Lock 而不是 RLock
当您调用无法控制的资源外部时。
下面的代码有两个变量:a 和 b,并且应该使用 RLock 来确保 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
the Lock 将是正确的选择,尽管它确实会阻塞。或者可以使用RLock._is_owned()
. changeAandB2
当您实现了观察者模式或发布者-订阅者并随后添加锁定时,可能会出现类似的功能。
这是 RLock 的另一个用例。假设您有一个支持并发访问的面向 Web 的用户界面,但您需要管理对外部资源的某些类型的访问。例如,您必须保持内存中的对象和数据库中的对象之间的一致性,并且您有一个管理器类来控制对数据库的访问,并且您必须确保以特定顺序调用方法,并且不能同时调用。
您可以做的是创建一个 RLock 和一个守护线程,通过不断获取它来控制对 RLock 的访问,并且仅在收到信号时才释放它。然后,确保您需要控制访问的所有方法都在运行之前获得锁。像这样的东西:
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
这样,您可以确保以下事项:
原语锁 (Lock) 是一种同步原语,在锁定时不属于特定线程。
对于可重复锁(RLock),在锁定状态下,某个线程拥有锁;在解锁状态下,没有线程拥有它。如果该线程已经拥有锁,则调用时,将递归级别加一,并立即返回。如果线程不拥有锁它会等到所有者释放锁。释放锁,递减递归级别。如果减量后为零,则将锁重置为解锁状态。
我不认为有一些性能差异,而是概念上的差异。