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

时间:2010-09-14 19:57:50

标签: python multithreading locking

我有一个只能通过静态方法从外部访问的类。然后,这些静态方法创建要在方法中使用的类的对象,然后它们返回并且可能会破坏对象。该类是几个配置文件的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访问我们的东西的团队抱怨并发问题。所以我不需要完美的解决方案,我只是想做到这一点,以便他们不会崩溃我们的一面或获取垃圾数据

5 个答案:

答案 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将为您获取并释放锁定,即使在执行文件操作时发生错误也是如此。