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

2 投票
5 回答
2745 浏览
提问于 2025-04-16 04:09

我有一个类,这个类只通过静态方法被外部访问。这些静态方法会创建这个类的对象,然后在方法内部使用这个对象,最后返回时这个对象就会被销毁。这个类主要是用来获取和设置一些配置文件的,现在我需要在访问这些配置文件时加上线程锁。

因为我有几个不同的静态方法都需要读写这些配置文件,并且它们都是在方法内部创建对象的,所以我在想是不是可以在对象的构造函数里加锁,然后在析构函数里释放锁。

我的同事对此表示担忧,觉得这样可能会导致类一直被锁住,如果发生了什么意外。他还提到在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时抱怨并发问题。所以我并不需要完美的解决方案,只是想确保他们不会让我们这边崩溃或者返回错误的数据。

5 个回答

1

不过,如果你真的必须在构造函数和析构函数中获取和释放锁,那你真的应该重新考虑一下你的设计思路。换句话说,你需要改变一些基本的假设。

在任何应用程序中,"锁"应该尽量保持的时间短短的——越短越好。这意味着,在大约90%的情况下,你会在同一个方法中获取锁,然后再释放锁。

几乎没有理由在RAII风格中锁定或解锁一个对象。这并不是它的本意;)

让我给你举个例子:假设你管理一些资源,这些资源可以被多个线程同时读取,但只有一个线程可以写入。

在一个“简单”的实现中,你会为每个对象设置一个锁,每当有人想写入时,就会锁住它。当多个线程想写入时,你会公平地进行同步,确保安全,但问题是:当某个线程说“写入”时,我们就会停滞,直到其他线程决定释放锁。

但请理解,锁和互斥量这些基本工具是为了同步你源代码中的少量行而创建的。所以,与其把锁作为可写对象的一部分,不如只在真正需要的时候短暂地使用锁。你需要在接口设计上投入更多的时间和思考。不过,锁和互斥量从来就不是为了被“持有”超过几微秒的。

3
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 语句 可以确保 (R)Lock 这个锁是成对使用的,也就是说,获取锁和释放锁是一起完成的。即使在 with 块中发生了错误,释放锁的操作也会被执行。

你可能还想考虑把锁放在文件访问的代码块 with open(...) as ... 周围,尽量紧凑一些,这样锁就不会被占用得太久。

最后,创建和垃圾回收 a=A() 这个对象不会影响锁的使用,前提是这些锁是类属性(而不是实例属性)。类属性存储在 A.__dict__ 中,而不是 a.__dict__。所以,锁会一直存在,直到整个类 A 被垃圾回收。

2

你说得对,垃圾回收的确不太好,所以这个主意不太合适。可以看看装饰器,用来编写同步函数。

举个例子:http://code.activestate.com/recipes/465057-basic-synchronization-decorator/

编辑 我还不是很确定你具体想要什么,所以我的建议可能不太对:

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()

撰写回答