【问题标题】:PyQt: QMutexLocker not released on exceptionPyQt:QMutexLocker 未在异常时释放
【发布时间】:2012-06-29 12:19:21
【问题描述】:

我有以下代码:

def query(self,query):
  lock = QMutexLocker(self.mutex)
  reply = self.conn.query(query)
  if (re.search("error", reply) != None):
    raise GeneralError("Query error")

  #more code...  
  return reply

现在,如果抛出异常,锁似乎没有被删除,导致互斥锁没有被释放。我当然可以到处做“del lock”,但这会带走qmutexlocker的全部意义。这与 Python 垃圾收集有关吗?如果是这样,那一定意味着 QMutexLocker 在 Python 中根本不可用?

【问题讨论】:

    标签: python pyqt


    【解决方案1】:

    您没有正确使用 QMutexLocker。像上下文管理器一样使用它:

    from PyQt4.QtCore import QMutex, QMutexLocker
    
    def bad_lock(aLock):
        locker = QMutexLocker(aLock)
        print "Locked"
        raise RuntimeError("Test exception")
    
        return "Should not get here"
    
    def good_lock(aLock):
        with QMutexLocker(aLock):
            print "Locked"
            raise RuntimeError("Test exception")
    
        return "Should not get here"
    
    lock = QMutex()
    
    bad_lock(lock)
    print lock.tryLock()
    # False
    
    lock.unlock()
    
    good_lock(lock)
    print lock.tryLock()
    # True
    

    在测试中,您在第一个示例中看到,锁返回仍处于锁定状态。第二种,当引发异常时,上下文管理器在离开函数之前释放锁。

    当在 C++ 中使用时,我确信 QMutexLocker 会做它应该做的事情,只要作用域结束就会解锁。但是在 Python 中,正如您所发现的,不应该依赖垃圾收集器来进行解锁。通过with 语句的上下文管理器非常适合这个。您可以通过此类的 C++ 示例显示它只是在函数顶部创建它的方式来判断。而 python 版本同时具有 __enter____exit__ 方法。

    最后,with 上下文允许您将关键代码块包装在锁中以限制需要到位的锁的数量,因此您可以执行以下操作:

    def good_lock(aLock):
    
        # do a bunch of stuff here
        ...
    
        # critical section
        with QMutexLocker(aLock):
            # do critical stuff here
            ...
    
        # do other stuff here
        ...
    
        return True
    

    【讨论】:

    • 很好,从来不知道 Py 中的 with 语句这么棒
    • @Rolle:显然不如这里的其他答案好,对吧? ;-) 那个告诉你 QMutexLocker 不可靠。
    • 你的解释对我来说很有意义。这很有趣,但即使是 PyQt 代码也错误地使用了 QMutexLocker:github.com/baoboa/pyqt5/blob/…
    • @jdg 至少这只是一个例子。但幸运的是,他们也没有在该函数体中提出自己的异常。只要没有引发意外异常并且调用者不希望访问异常处理程序中的锁定资源,它在技术上仍然可以正常工作。
    【解决方案2】:

    如果您希望在引发异常之前释放互斥锁,请释放它:

    def query(self, query):
        lock = QMutexLocker(self.mutex)
        reply = self.conn.query(query)
        if re.search("error", reply):
            lock.unlock()
            raise GeneralError("Query error")
    

    如果您希望当lock 超出范围时会立即释放,那么您对解释器的期望过高。既然您确切地知道何时以及为什么应该释放锁,那就去做吧。

    作为一般规则 - Python 或其他地方 - 您应该始终将互斥锁绑定到尽可能小的操作。我假设您知道任何查询实际上都需要保护,并且在调用 self.conn.query 之后仍然需要它。

    针对评论添加

    这是一个公平的观点,“一定意味着 QMutexLocker 根本不可用”,我确实错过了。我假设您指的是PySide.QtCore.QMutexLocker,这不太可能:

    现在,互斥锁将始终在 PySide.QtCore.QMutexLocker 对象被销毁(当函数 返回,因为 locker 是一个自动变量)。

    这不太可能,因为 Python 中没有 auto 变量存储类之类的东西。我怀疑这将在进一步调查后证明是“让我们包装一个 C++ 库并假设范围语义工作”。如果这个猜测是正确的,您可以使用with statement 来更好地保证可靠的解锁。

    【讨论】:

    • 谢谢,我担心它是这样的,因为 Python 是 GC-ed。然而,正如我所提到的,这使得 QMutexLocker 的整个想法变得毫无价值,因为它的重点是不必担心解锁。当我不抛出异常时,它似乎也有效,这有点奇怪。关于不知道何时删除锁应该有同样的问题吗?
    • 我觉得这个答案有点笼统,并且绕过了正确的信息,只是做出了假设。毫无疑问,如果使用得当,QMutexLocker 是可用的。但我完全同意仅锁定关键代码部分以最小化锁定。
    • 同意,我只是在猜测,这就是为什么我赞成你的回答。
    • 我认为您的答案在于您触及 C++ 对象和 PyQt 包装版本之间的差异的方式。还有垃圾收集与“自动”
    • 这是一个非常糟糕的答案。with: 上下文之外使用QMutexLocker 会使使用QMutexLocker 的全部意义无效。如果你只是要这样做,你还不如使用更轻的QMutex。当然,你也不应该这样做那个;您应该只在 with: 上下文中使用 QMutexLocker。尝试从引发的异常中恢复时,任何其他锁定模式都会引发竞争条件和死锁。
    猜你喜欢
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    相关资源
    最近更新 更多