python中构造函数/析构函数中的线程锁定/解锁

python中构造函数/析构函数中的线程锁定/解锁,python,multithreading,locking,Python,Multithreading,Locking,我有一个只能通过静态方法从外部访问的类。然后,这些静态方法创建类的一个对象以在方法中使用,然后返回,对象可能被销毁。该类是几个配置文件的getter/setter,现在我需要在访问配置文件时放置线程锁 由于我有几个不同的静态方法,它们都需要对所有在方法范围内创建对象的配置文件进行读/写访问,因此我考虑在对象构造函数中完成锁获取,然后在析构函数中释放 我的同事表示担心,如果发生了什么事情,可能会使类永远锁定。他还提到了python中的析构函数是如何调用垃圾收集器的,但我们都是python的新手,所

我有一个只能通过静态方法从外部访问的类。然后,这些静态方法创建类的一个对象以在方法中使用,然后返回,对象可能被销毁。该类是几个配置文件的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访问我们的东西的团队抱怨并发性问题。所以我不需要完美的解决方案,我只是想让他们不会破坏我们的团队或回收垃圾数据。垃圾收集是正确的,所以这不是一个好主意。 查看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()

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

既然您提到您是python新手,我必须说,大多数情况下线程不是python中的解决方案。如果您的活动是CPU绑定的,则应该考虑使用。因为GIL没有并发执行,记得吗?(大多数情况下都是如此)。如果您的活动是I/O绑定,我想是这样的话,那么您应该考虑使用事件驱动的框架,比如。这样你就不用担心死锁了,我保证:)

使用将保证成对获取和释放(R)锁。即使with块中发生异常,也将调用释放

您可能还想考虑将锁尽可能紧密地围绕文件访问块
放置,打开(…)为…
,这样锁的持有时间就不会超过需要的时间

最后,a=a()的创建和垃圾收集不会影响锁
如果(如上所述)锁是类属性(与实例属性相反)。类属性位于
A.\uu dict\uu
中,而不是
A.\uu dict\uu
。因此,在对自身进行垃圾收集之前,不会对锁进行垃圾收集。

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

如果在类实例中处理一个特定的configfile,那么可以将线程模块中的锁对象放入其中。 这方面的一些示例代码:

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')
来自线程导入锁
类配置文件:
定义初始化(文件):
self.file=文件
self.lock=lock()
def写入(自身、数据):
self.lock.aquire()
self.lock.release()
#使用ConfigFile对象的函数
def staticmethod():
config=ConfigFile('myconfig.conf')
config.write('some data')
您还可以在With语句中使用锁,例如:

def write(self, data):
  with self.lock:
    <do stuff with file>
def写入(自身、数据):
使用self.lock:

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

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

在任何应用程序中:一个“锁”应该总是保持很短的时间-尽可能短。这意味着-在可能90%的情况下,您将以同样的方法获得锁,也将释放锁

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

让我给你举个例子:你管理一些资源,这些资源可以一次从多个线程读取,但只有一个线程可以写入它们

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


但是请理解,锁、互斥锁——所有这些原语都是为了同步源代码的几行而创建的。因此,与使锁成为可写对象的一部分不同,您只有一个真正需要锁的很短时间内的锁。您必须在界面上投入更多的时间和精力。但是,锁/互斥锁的“保持”时间决不能超过几微秒。

这是一个更大项目的一部分,我只真正了解我的密码
def write(self, data):
  with self.lock:
    <do stuff with file>