何时以及如何使用Python的RLock

Awa*_*ias 46 python locking python-2.7

通过Python文档阅读我遇到了RLock.

有人可以向我(例子)解释一个RLock比较喜欢的场景Lock吗?

特别是:

  • RLock的"递归水平".这有用吗?
  • RLock对象的线程"所有权"
  • 性能?

Use*_*ser 51

这是我看到使用的一个例子:

有用的时候

  1. 您希望从类外部获得线程,并使用类中的相同方法:

    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()
    
    Run Code Online (Sandbox Code Playgroud)
  2. 对于递归更明显:

    lock = threading.RLock()
    def a(...):
         with lock:
    
             a(...) # somewhere inside
    
    Run Code Online (Sandbox Code Playgroud)

    其他线程必须等到第一次a完成调用=线程所有权.

性能

通常我开始使用Lock进行编程,当出现情况1或2时,我切换到RLock.在Python 3.2之前,由于附加代码,RLock应该有点慢.它使用Lock:

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()
Run Code Online (Sandbox Code Playgroud)

线程所有权

在给定的线程中,您可以随意获取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
Run Code Online (Sandbox Code Playgroud)

changeAandB2Lock中,它会是正确的选择,尽管它会阻止.或者可以使用错误来增强它RLock._is_owned().changeAandB2当您实现了Observer模式或Publisher-Subscriber并在之后添加锁定时,可能会出现类似的函数.

  • 对于未来的读者,在[Python 3.2及更高版本中,"RLock"的性能成本基本为零](https://docs.python.org/3/whatsnew/3.2.html#optimizations),因为`RLock`已实现在C中就像`Lock`; 以前,它更慢,因为它执行了很多Python代码来包装`Lock`,但在3.2+中,没有成本(除了`Lock`可以从其他线程解锁,其中`RLock`只能由所有者解锁,这是一个有意义的区别,比如使用跨线程解锁来通知服务员的`Condition`的实现. (3认同)

小智 6

这是 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
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以确保以下事项:

  1. 线程一旦获得资源锁,就可以自由调用资源管理器的protected方法,因为RLock是递归的
  2. 一旦线程通过面向 Web 的接口中的 master 方法获取资源锁,所有对管理器中受保护方法的访问都会被其他线程阻塞
  3. 管理器中受保护的方法只能通过首先向监护人发出请求才能访问。


ole*_*leg 5

  • 递归级别
  • 所有权

原语锁(Lock)是一种同步原语,在锁定时不属于特定线程。

对于可重复锁(RLock),在锁定状态下,某个线程拥有该锁;在解锁状态下,没有线程拥有它。如果此线程已拥有锁,则调用时,将递归级别增加 1,并立即返回。如果线程不拥有锁,它会等待所有者释放锁。释放锁,递减递归级别。如果递减后为零,则将锁重置为解锁状态。

  • 表现

我不认为有一些性能差异而不是概念上的差异。