为啥 PyQt 在没有信息的情况下会崩溃? (退出代码 0xC0000409)

Posted

技术标签:

【中文标题】为啥 PyQt 在没有信息的情况下会崩溃? (退出代码 0xC0000409)【英文标题】:Why does PyQt crashes without information? (exit code 0xC0000409)为什么 PyQt 在没有信息的情况下会崩溃? (退出代码 0xC0000409) 【发布时间】:2018-03-24 10:22:13 【问题描述】:

我正在尝试使用 PyQt 开发软件,但我经常遇到没有调试信息的软件崩溃(只有退出代码 0xC0000409)。我正在使用QThread,我写了一个这样的系统:

class serialThreadC(QThread):
    updateOutBox = QtCore.pyqtSignal(str)
    updateStatus = QtCore.pyqtSignal(int)

    def __init__(self):
        super(serialThreadC, self).__init__()
        self.ser = False
        self.state = 0
        self.serialEnabled = False

    def run(self):
        while True:
            if self.state == -3 or self.state == -2:
                if self.SerialEnabled:
                    self.updatePB(20)
            elif self.state == 0:
                if self.serialEnabled:
                    self.updatePB(20)

    def ConnDisconn(self):
        self.serialEnabled = not self.serialEnabled

    def updatePB(self, stat):
        self.state = stat
        self.updateStatus.emit(self.state)

serialThread = serialThreadC()
serialThread.start()

## sw is a QDialog already loaded
serialThread.updateOutBox.connect(sw.updateOutBox)
serialThread.updateStatus.connect(sw.updateStatus)

sw.PB_ConnDisconn.clicked.connect(serialThread.ConnDisconn)

当我在run()ConnDisconn() 中读/写serialEnabled 时发生崩溃。我知道 PyQt 不是线程安全的,并且对变量的错误处理会导致我的类型崩溃,但我不明白我的代码有什么问题。我的想法(可能是错误的)是所有serialThread 方法都在同一个线程上执行,即使它们连接到 gui(主线程)也是如此。那是错的吗?同样,我从 serialThread 发出事件并将它们连接到 GUI,但这从来没有给我带来问题。

你能看出我犯的错误吗?如果在没有其他信息的情况下发生崩溃,有没有办法调试代码? (我使用 PyCharm 2017.1.3)。

【问题讨论】:

你试过从终端运行吗? 这是真的!在终端我有崩溃的原因:|而且我浪费了大约 8 个小时来调试没有信息的代码......在那种情况下,python 似乎无法理解我对两个类似函数 updatePB(self,stat)和 updatePB(self)所做的重载,因为我给了 2 个参数而哭泣当我调用它时,而不是 1。 谢谢@eyllanesc!我试图使用 PyCharm 的运行/调试配置运行代码,但只得到错误代码。 【参考方案1】:

PyQt 的线程安全程度与 Qt 的线程安全程度相同。 Qt 文档会告诉您他们的 API 的哪些部分可以保证,以及在什么情况下。

跨线程信号是线程安全的,因此在您的示例中调用updatePB 方法是可以的。您的 ConnDisconn 方法不是线程安全的,但这与 PyQt 或 Qt 无关 - 这只是您编写它的方式的结果。 serialEnabled 属性可以由两个线程同时读取/写入,因此行为是严格未定义的。编写此代码的线程安全方式是使用mutex,如下所示:

class serialThreadC(QThread):
    updateOutBox = QtCore.pyqtSignal(str)
    updateStatus = QtCore.pyqtSignal(int)

    def __init__(self):
        super(serialThreadC, self).__init__()
        self.ser = False
        self.state = 0
        self._mutex = QMutex()
        self.serialEnabled = False   

    def ConnDisconn(self):
        self._mutex.lock()
        self.serialEnabled = not self.serialEnabled
        self._mutex.unlock()

    def run(self):
        while True:
            if self.state == -3 or self.state == -2:
                self._mutex.lock()
                if self.serialEnabled:
                    self.updatePB(20)
                self._mutex.unlock()
            elif self.state == 0:
                self._mutex.lock()
                if self.serialEnabled:
                    self.updatePB(20)
                self._mutex.unlock()

(注意:如果您使用任何类型的 IDE 或调试器,并且遇到意外错误或崩溃,那么诊断问题的第一步应该始终是在标准控制台中测试代码。通常情况下, IDE 或调试器本身可能是问题的原因,也可能会掩盖来自 Python 或底层库(如 Qt)的错误消息。

【讨论】:

我不明白这一点。既然您告诉ConnDisconn(self) 是与serialThread 不同的线程(这是有道理的,ConnDisconn 连接到 GUI 信号),sw.updateStatus 也应该是相同的(不在主线程中)。在sw.updateStatusMyButton.setText(),这个操作可以吗?还有,是不是也禁止不同线程同时读取变量? @brazoayeye。我没有说ConnDisconn 在不同的线程中(它不是,它存在于主线程中,并由主线程调用)。但我确实说过updatePB 没问题,因为它发出一个跨线程信号,这保证(由Qt)是线程安全的。所以是的,作为直接结果,调用setText() 是安全的。这些都是 PyQt 中的标准内容,在 SO 上有很多类似的问题。永远禁止通过多个线程读取/写入anything - 这就是整个问题! (这就是互斥锁的用途)。 @brazoayeye。不过,我应该补充一点,对于您的具体示例,互斥锁可能是多余的,因为(我假设)只有一个线程可以修改 serialEnabled(即主线程)。所以你的示例代码看起来不错,即使它不是 100% 严格正确。就修复代码而言,我的答案中最重要的部分可能是底部的注释。其余的只是试图回答您提出的更一般的问题。 如果ConnDisconn 存在于主线程中,因为它是由带有信号槽的主线程调用的,为什么sw.updateStatus 不存在于serialThread 中,因为它是由serialThread 调用的一个几乎相同的方式?您可以发布最相关的问题教程以加深主题吗? @brazoayeye。因为serialThread 也存在于主线程中。 QThread 类本身并不是一个线程——它只是一个管理操作系统提供的底层线程的类。所以你的serialThread 及其方法 all 都存在于主线程中。您的示例中唯一在主线程之外执行的部分是 run 方法中的代码。这会调用updatePB,它会发出一个信号。 Qt 会自动检测信号何时从一个线程发送到另一个线程,并且以线程安全的方式(通过将事件发布到接收线程的事件循环)进行检测。【参考方案2】:

我将所有浮点变量设置为 int 值并且它起作用了

之前


var1 = 10.0

var2 = 26.0

之后


var1 = 10

var2 = 26

【讨论】:

以上是关于为啥 PyQt 在没有信息的情况下会崩溃? (退出代码 0xC0000409)的主要内容,如果未能解决你的问题,请参考以下文章

为啥 PyQt 有时会在退出时崩溃?

Windows 上的 PyQt4 应用程序在退出时崩溃

为啥在 macOS 上使用 QThread 时 PyQt 应用程序崩溃或挂起?

为啥在一种情况下会收到带有字符串文字的不推荐使用的转换警告,而在另一种情况下却没有?

在啥情况下会在控制权到达 main() 函数之前发生崩溃? [复制]

什么情况下会出现sigabrt崩溃,如何调试