我有一个只能通过静态方法从外部访问的类。然后,这些静态方法创建要在方法中使用的类的对象,然后它们返回并且可能会破坏对象。该类是几个配置文件的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访问我们的东西的团队抱怨并发问题。所以我不需要完美的解决方案,我只是想做到这一点,以便他们不会崩溃我们的一面或获取垃圾数据
答案 0 :(得分: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 statement将保证(R)Lock成对获取和释放。即使在with-block中发生异常,也会调用该版本。
您可能还想考虑尽可能紧密地将锁定放在文件访问块with open(...) as ...
周围,以便锁定时间不会超过必要的时间。
最后,a = A()的创建和垃圾收集不会影响锁
如果(如上所述)锁是类属性(而不是实例属性)。类属性位于A.__dict__
,而不是a.__dict__
。因此,在A本身被垃圾收集之前,锁不会被垃圾收集。
答案 1 :(得分:1)
你对垃圾收集是正确的,所以这不是一个好主意。 查看装饰器,以编写同步函数。
示例: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()
答案 2 :(得分:1)
但是,如果你必须获取并释放构造函数和析构函数中的锁,那么你真的,真的,真的应该给你的设计另一个机会。你应该改变你的基本假设。
在任何应用中:“LOCK”应始终保持短时间 - 尽可能短。这意味着 - 在所有情况的90%中,您将使用同样的方法获取锁定,这也将释放锁定。
在RAII风格中锁定/解锁对象几乎没有任何理由。这不是它的意思;)
让我给你一个例子:你管理一些资源,那些资源。可以一次从多个线程中读取,但只有一个线程可以写入它们。
在“天真”的实现中,每个对象都有一个锁定,每当有人想要写入它时,你就会锁定它。当多个线程想要写入它时,那么你可以公平地同步它,一切都很安全,但是:当线程说“WRITE”时,我们将停止,直到其他线程决定释放锁。
但请理解lock,mutex - 所有这些原语都是为了同步源代码的几行而创建的。因此,不是将锁定部分作为可写对象的一部分,而是只需要在非常短的时间内锁定它。您必须在界面中投入更多时间和精力。但是,LOCKS / MUTEXES从未打算“保持”超过几微秒。
答案 3 :(得分:0)
我不知道你在哪个平台上,但如果你需要锁定一个文件,那么你应该使用flock()
(如果它可用)而不是滚动你自己的锁定程序。
既然你已经提到你是python的新手,我必须说大多数时候线程不是 python中的解决方案。如果您的活动受CPU限制,则应考虑使用multiprocessing。由于GIL没有并发执行,还记得吗? (大多数情况都是这样)。如果您的活动是I / O绑定的,我认为是这种情况,您应该考虑使用事件驱动的框架,如Twisted。这样你根本不用担心死锁,我保证:)
答案 4 :(得分:0)
由于垃圾收集器已经提到,释放对象销毁中的锁是有风险的,因为决定何时在对象上调用__del__()
方法完全由GC决定(通常当refcount达到零时) )但在某些情况下,如果你有循环引用,它可能永远不会被调用,即使程序退出。
如果要处理类实例中的一个特定配置文件,那么可以在其中的Threading模块中放置一个锁定对象。 一些示例代码:
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将为您获取并释放锁定,即使在执行文件操作时发生错误也是如此。