Python中构造函数/析构函数中的线程锁定/解锁
我有一个类,这个类只通过静态方法被外部访问。这些静态方法会创建这个类的对象,然后在方法内部使用这个对象,最后返回时这个对象就会被销毁。这个类主要是用来获取和设置一些配置文件的,现在我需要在访问这些配置文件时加上线程锁。
因为我有几个不同的静态方法都需要读写这些配置文件,并且它们都是在方法内部创建对象的,所以我在想是不是可以在对象的构造函数里加锁,然后在析构函数里释放锁。
我的同事对此表示担忧,觉得这样可能会导致类一直被锁住,如果发生了什么意外。他还提到在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 个回答
不过,如果你真的必须在构造函数和析构函数中获取和释放锁,那你真的应该重新考虑一下你的设计思路。换句话说,你需要改变一些基本的假设。
在任何应用程序中,"锁"应该尽量保持的时间短短的——越短越好。这意味着,在大约90%的情况下,你会在同一个方法中获取锁,然后再释放锁。
几乎没有理由在RAII风格中锁定或解锁一个对象。这并不是它的本意;)
让我给你举个例子:假设你管理一些资源,这些资源可以被多个线程同时读取,但只有一个线程可以写入。
在一个“简单”的实现中,你会为每个对象设置一个锁,每当有人想写入时,就会锁住它。当多个线程想写入时,你会公平地进行同步,确保安全,但问题是:当某个线程说“写入”时,我们就会停滞,直到其他线程决定释放锁。
但请理解,锁和互斥量这些基本工具是为了同步你源代码中的少量行而创建的。所以,与其把锁作为可写对象的一部分,不如只在真正需要的时候短暂地使用锁。你需要在接口设计上投入更多的时间和思考。不过,锁和互斥量从来就不是为了被“持有”超过几微秒的。
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 被垃圾回收。
你说得对,垃圾回收的确不太好,所以这个主意不太合适。可以看看装饰器,用来编写同步函数。
举个例子: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()