何时以及如何使用Python的RL

2024-06-02 11:38:30 发布

您现在位置:Python中文网/ 问答频道 /正文

阅读我遇到的Python文档RLock

有人能给我解释一下(举个例子)一个^{}^{}更受欢迎的场景吗?

特别提及:

  • RLock的“递归级别”。这有什么用?
  • 一个RLock对象的线程“所有权”
  • 表演?

Tags: 对象文档场景级别rlock线程例子所有权
3条回答

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

  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()
    
  2. 对于更明显的递归:

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

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

性能

通常,我用锁开始编程,当情况1或2发生时,我切换到RLock。Until 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()

线程所有权

在给定的线程中,您可以随时获取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中,尽管锁确实会阻塞,但它是正确的选择。或者可以使用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

这样,您就可以确保以下几点:

  1. 一旦线程获得了资源锁,它就可以自由地调用资源管理器的受保护方法,因为RLock是递归的
  2. 一旦线程通过面向web的接口中的master方法获取资源锁,管理器中对受保护方法的所有访问都将被其他线程阻止
  3. 管理器中受保护的方法只能通过首先向监护人提出请求来访问。
  • 递归级别
  • 所有权

基元锁(lock)是一种同步基元,锁定时不属于特定线程。

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

  • 性能

我不认为有什么性能上的差异,而是概念上的差异。

相关问题 更多 >