何时以及如何使用 Python 的 RLock
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16567958/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When and how to use Python's RLock
提问by Awalias
Reading through the Python docs I came across RLock.
通读我遇到的 Python 文档RLock。
Can someone explain to me (with example) a scenario in which RLockwould be preferred to Lock?
有人可以向我解释(举例)一个RLock更喜欢的场景Lock吗?
With particular reference to:
特别是:
RLock's “recursion level”. How is this useful?- A threads "ownership" of an
RLockobject - Performance?
RLock的“递归级别”。这有什么用?RLock对象的线程“所有权”- 表现?
采纳答案by User
This is one example where I see the use:
这是我看到使用的一个示例:
Useful when
有用的时候
you want to have thread-safe access from outside the class and use the same methods from inside the class:
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()for recursion more obvious:
lock = threading.RLock() def a(...): with lock: a(...) # somewhere insideother threads have to wait until the first call of
afinishes = thread ownership.
您希望从类外部进行线程安全访问并在类内部使用相同的方法:
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完成 = 线程所有权。
Performance
表现
Usually, I start programming with the Lock and when case 1 or 2 occur, I switch to an RLock. Until Python 3.2the RLock should be a bit slower because of the additional code. It uses Lock:
通常,我从 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()
Thread Ownership
线程所有权
within the given thread you can acquire a RLockas often as you like. Other threads need to wait until this thread releases the resource again.
在给定的线程中,您可以随心所欲地获取一个RLock。其他线程需要等待,直到该线程再次释放资源。
This is different to the Lockwhich implies 'function-call ownership'(I would call it this way): Another function call has to wait until the resource is released by the last blocking function even if it is in the same thread = even if it is called by the other function.
这与Lock隐含“函数调用所有权”的意思不同(我会这样称呼它):另一个函数调用必须等到资源被最后一个阻塞函数释放,即使它在同一个线程中=即使它被另一个函数调用。
When to use Lock instead of RLock
何时使用 Lock 而不是 RLock
When you make a call to the outside of the resource which you can not control.
当您调用您无法控制的资源的外部时。
The code below has two variables: a and b and the RLock shall be used to make sure a == b * 2
下面的代码有两个变量: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 changeAandB2the Lock would be the right choice although it does block. Or one can enhance it with errors using RLock._is_owned(). Functions like changeAandB2may occur when you have implemented an Observer pattern or a Publisher-Subscriber and add locking afterward.
changeAandB2尽管它确实阻塞,但在锁定中将是正确的选择。或者可以使用RLock._is_owned(). changeAandB2当您实现了观察者模式或发布者-订阅者并在之后添加锁定时,可能会出现类似的功能。
回答by oleg
- recursion level
- ownership
- 递归级别
- 所有权
A primitive lock (Lock) is a synchronization primitive that is not owned by a particular thread when locked.
原语锁(Lock)是一种同步原语,在锁定时不属于特定线程。
For the repeatable Lock (RLock) In the locked state, some thread owns the lock; in the unlocked state, no thread owns it. When invoked if this thread already owns the lock, increment the recursion level by one, and return immediately. if thread doesn't own the lock It waits until owner release lock. Release a lock, decrementing the recursion level. If after the decrement it is zero, reset the lock to unlocked.
对于可重复锁(RLock),在锁定状态下,某个线程拥有该锁;在解锁状态下,没有线程拥有它。如果此线程已拥有锁,则调用时,将递归级别增加 1,并立即返回。如果线程不拥有锁,它会等待所有者释放锁。释放锁,递减递归级别。如果递减后为零,则将锁重置为解锁状态。
- Performance
- 表现
I don't think there is some performance difference rather conceptual one.
我不认为有一些性能差异而不是概念上的差异。
回答by Corey Moncure
Here is another use case for RLock. Suppose you have a web-facing user interface that supports concurrent access, but you need to manage certain kinds of access to an external resource. For instance, you have to maintain consistency between objects in memory and objects in a database, and you have a manager class that controls access to the database, with methods that you must ensure get called in a specific order, and never concurrently.
这是 RLock 的另一个用例。假设您有一个支持并发访问的面向 Web 的用户界面,但您需要管理对外部资源的某些类型的访问。例如,您必须保持内存中的对象和数据库中的对象之间的一致性,并且您有一个管理器类来控制对数据库的访问,其中的方法必须确保按特定顺序调用,并且不能同时调用。
What you can do is create an RLock and a guardian thread that controls access to the RLock by constantly acquiring it, and releasing only when signaled to. Then, you ensure all methods you need to control access to are made to obtain the lock before they run. Something like this:
你可以做的是创建一个 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
This way, you're ensured of the following things:
通过这种方式,您可以确保以下事项:
- Once a thread acquires the resource lock, it can call the resource manager's protected methods freely, because RLock is recursive
- Once the thread acquires the resource lock through the master method in the web facing interface, all access to protected methods in the manager will be blocked to other threads
- Protected methods in the manager can only be accessed by first appealing to the guardian.
- 线程一旦获得资源锁,就可以自由调用资源管理器的protected方法,因为RLock是递归的
- 一旦线程通过面向 Web 的接口中的 master 方法获取资源锁,所有对管理器中受保护方法的访问都会被其他线程阻塞
- 管理器中受保护的方法只能通过首先向监护人发出请求才能访问。

