何时以及如何使用Python RLock

时间:2013-05-15 14:34:56

标签: python python-2.7 locking

通过Python文档阅读RLock

有人可以向我(例如)说明RLock优先于Lock的情况吗?

特别提到:

  • RLock的“递归级别”。这有用吗?
  • RLock对象
  • 的线程“所有权”
  • 性能?

3 个答案:

答案 0 :(得分:42)

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

时有用
  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 threadsave!
            with self.lock:
                self.changeA() # a usual lock would block in here
                self.changeB()
    
  2. 递归的
  3. 更明显:

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

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

  4. <强>性能

    通常我开始使用Lock进行编程,当出现情况1或2时,我切换到RLock。 Until 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()
    

    主题所有权

    在给定的帖子中,您可以随意获取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
    

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

答案 1 :(得分:3)

  • 递归级别
  • 所有权

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

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

  • 性能

我不认为存在一些性能差异而非概念性差异。

答案 2 :(得分:3)

这是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

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

  1. 一旦线程获得资源锁,它就可以自由地调用资源管理器的受保护方法,因为RLock是递归的
  2. 一旦线程通过面向Web的界面中的主方法获取资源锁定,管理器中对受保护方法的所有访问都将被阻止到其他线程
  3. 管理员中的受保护方法只能通过首先吸引监护人来访问。