【问题标题】:thread Locking/unlocking in constructor/destructor in pythonpython中的构造函数/析构函数中的线程锁定/解锁
【发布时间】:2010-09-14 19:57:50
【问题描述】:

我有一个只能通过静态方法从外部访问的类。然后这些静态方法创建类的对象以在方法中使用,然后它们返回并且对象可能被销毁。该类是几个配置文件的 getter/setter,现在我需要在对配置文件的访问上放置线程锁。

由于我有几个不同的静态方法都需要对所有在方法范围内创建对象的配置文件进行读/写访问,我正在考虑在对象构造函数内部完成锁定获取,然后释放在析构函数中。

我的同事表示担心,如果发生某些事情,这似乎可能会使班级永远锁定。他还提到了关于垃圾收集器如何调用 python 中的析构函数,但我们都对 python 比较陌生,所以这是一个未知数。

这是一个合理的解决方案,还是我应该在每个方法本身中锁定/解锁?


Class A():
    rateLock = threading.RLock()
    chargeLock = threading.RLock()

    @staticmethod
    def doZStuff():
        a = A()
        a.doStuff('Z')

    @staticmethod
    def doYStuff():
        a = A()
        a.doStuff('Y')

    @synchronized(lock)
    def doStuff(self, type):
        if type == 'Z':
            otherstuff()
        elif type == 'B':
            evenmorestuff()

是否有可能让它与doStuff() 上的装饰器而不是doZStuff() 上的装饰器一起工作

更新

感谢大家的回答。我面临的问题主要是因为异步访问我的模块并没有什么意义,但这只是 API 的一部分。通过 API 访问我们的东西的团队抱怨并发问题。所以我不需要完美的解决方案,我只是努力做到这一点,这样他们就不会崩溃我们的一方或取回垃圾数据

【问题讨论】:

  • 请注意,将这些静态函数放在模块范围内会更简洁,然后将类定义视为完全私有的,即模块的实现细节。

标签: python multithreading locking


【解决方案1】:
Class A():
    rateLock = threading.RLock()
    chargeLock = threading.RLock()

    def doStuff(self,ratefile,chargefile):
        with A.rateLock:
            with open(ratefile) as f:
                # ...
        with A.chargeLock:
            with open(chargefile) as f:
                # ...

使用with statement 将保证成对获取和释放(R)Lock。即使with-block内发生异常,也会调用release。

您可能还想考虑将锁尽可能紧地放在文件访问块with open(...) as ... 周围,这样锁的持有时间不会超过必要的时间。

最后a=A()的创建和垃圾回收不会影响锁 如果(如上所述)锁是类属性(而不是实例属性)。类属性位于A.__dict__,而不是a.__dict__。所以在 A 本身被垃圾回收之前,锁不会被垃圾回收。

【讨论】:

  • +1 如果经常使用 with 语句,可以将它们包装在装饰器中。
【解决方案2】:

您对垃圾收集的看法是正确的,所以这不是一个好主意。 查看装饰器,用于编写同步函数。

示例:http://code.activestate.com/recipes/465057-basic-synchronization-decorator/

编辑 我仍然不能 100% 确定您的想法,所以我的建议可能是错误的:

class A():
    lockZ = threading.RLock()
    lockY = threading.RLock()

    @staticmethod
    @synchroized(lockZ)
    def doZStuff():
        a = A()
        a.doStuff('Z')

    @staticmethod
    @synchroized(lockY)
    def doYStuff():
        a = A()
        a.doStuff('Y')

    def doStuff(self, type):
        if type == 'Z':
            otherstuff()
        elif type == 'B':
            evenmorestuff()

【讨论】:

  • 我从来没有真正理解过装饰器。我会更新我的问题,你能告诉我一个如何修改该代码以能够有条件地锁定任一锁的示例吗?
  • 你的关键部分是什么? rateLock 和chargeLock 是什么?您应该锁定在尽可能低的级别。
  • 该类可用于获取/设置需要完全单独锁定的 2 个不同配置文件的配置选项。
【解决方案3】:

但是,如果您必须在构造函数和析构函数中获取和释放锁,那么您真的、真的、真的应该再给您的设计一次机会。你应该改变你的基本假设。

在任何应用程序中:“LOCK”应始终保持很短的时间 - 尽可能短。这意味着 - 在大约 90% 的情况下,您将使用释放锁的相同方法获取锁。

几乎没有理由以 RAII 样式锁定/解锁对象。这不是它的本意;)

让我给你一个例子:你管理一些资源,那些资源。可以同时从多个线程中读取,但只有一个线程可以写入。

在一个“幼稚”的实现中,每个对象都有一个锁,当有人想要写入它时,你就会锁定它。当多个线程想要写入它时,您可以公平地同步它,一切都安全且良好,但是:当线程说“WRITE”时,我们将停止,直到其他线程决定释放锁。

但请理解,锁、互斥锁 - 所有这些原语都是为了同步您的源代码的几行而创建的。因此,与其使锁成为可写对象的一部分,不如在真正需要它的很短的时间内拥有一个锁。您必须在界面上投入更多时间和思想。但是,LOCKS/MUTEXES 永远不会被“持有”超过几微秒。

【讨论】:

  • 这些都是好点。我想锁不是处理我需要管理的资源的最佳方式,但我有点卡在我能做的事情上。我猜我对构造函数/析构函数锁定的想法是实现队列的一种骇人听闻的方式
【解决方案4】:

我不知道您在哪个平台上,但是如果您需要锁定文件,那么您应该使用flock()(如果可用)而不是滚动您自己的锁定例程。

既然你提到你是python的新手,我必须说大多数时候线程不是在python中的解决方案。如果您的活动受 CPU 限制,则应考虑使用 multiprocessing。因为 GIL 没有并发执行,记得吗? (大多数情况下都是如此)。如果您的活动受 I/O 限制(我猜是这种情况),您或许应该考虑使用事件驱动框架,例如 Twisted。这样你就不用担心死锁了,我保证:)

【讨论】:

  • 这是一个更大的项目的一部分,我只对我的部分有真正的了解。我真的无法改变任何东西,我只是被告知我们需要锁定。问题是我需要读-处理-写锁定,所以仅仅锁定文件是不够的。
  • @Falmarri:嗯,我看到你有你的要求等等,但你从来没有在问题中陈述过。我想我会在这里留下我的答案,因为我认为我的观点仍然非常有效。
  • @Falmarri:顺便问一下,静态方法是怎么回事?你有什么理由应该使用它们而不是普通函数吗?
  • 我不确定是否有 100% 合理的理由,但这就是我正在使用的,这就是项目的构建方式。我认为这主要是在尝试使用纯粹的 OO 范式
  • 听起来是个非常缺乏经验的设计师。
【解决方案5】:

在销毁对象时释放锁是有风险的,因为垃圾收集器已经提到过,因为决定何时对对象调用 __del__() 方法完全由 GC 决定(通常在 refcount 达到零时),但是在某些情况下,如果您有循环引用,即使程序退出,它也可能永远不会被调用。

如果您在类实例中处理一个特定的配置文件,那么您可能会在其中放置一个来自线程模块的锁定对象。 一些示例代码:

from threading import Lock

class ConfigFile:
  def __init__(file):
    self.file = file
    self.lock = Lock()

  def write(self, data):
    self.lock.aquire()    
    <do stuff with file>
    self.lock.release()

# Function that uses ConfigFile object

def staticmethod():
    config = ConfigFile('myconfig.conf')
    config.write('some data')

您还可以在 With 语句中使用锁,例如:

def write(self, data):
  with self.lock:
    <do stuff with file>

即使在处理文件时发生错误,Python 也会为您获取和释放锁。

【讨论】:

  • 我喜欢你在 ConfigFile 类中使用锁的想法。但是,我认为 Lock() 需要传递给 ConfigFile.__init__ 方法,而不是成为实例属性。就目前而言,每个 ConfigFile 实例都有自己的锁,这违背了目的......
  • 每个调用静态函数的线程都会得到一个新对象和一个新的解锁锁!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-18
  • 2015-06-30
  • 1970-01-01
  • 1970-01-01
  • 2016-09-09
相关资源
最近更新 更多