Python在PyQt5应用程序中崩溃了2个工作线程

时间:2017-04-03 15:05:30

标签: python multithreading python-3.x pyqt5

我想在我的应用程序中有2个工作线程。一旦GUI加载就应该开始运行,另一个应该稍后通过一些信号启动。让我们说它是一个按钮点击。

当我的Python解释器在执行第二个线程时崩溃(如显示Windows错误" Python停止工作",没有堆栈跟踪)时,我遇到了一种奇怪的行为。

这是一个例子,在我点击按钮后会立即崩溃。

class Worker(QtCore.QThread):
    def __init__(self, method_to_run):
        super().__init__()
        self.method = method_to_run

    def run(self):
        self.method()

class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QPushButton('Test', self)
        self.label = QLabel(self)
        self.button.clicked.connect(self.handleButton)
        layout = QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self.worker = Worker(self.test_method)
        self.worker.start()

    def handleButton(self):
        self.label.setText('Button Clicked!')
        worker = Worker(self.test_method)
        worker.start() 

    @staticmethod
    def test_method():
        res = [i*i for i in range(100500)]

if __name__ == '__main__':

    import sys
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

更奇怪的是,当您因某种原因调试应用程序时它不会崩溃。

我在这里缺少什么?

修改 我可以从故障转移中得到很多,因为我没有QT的符号。但看起来崩溃发生在QtCore.dll

ExceptionAddress: 00000000632d4669 (Qt5Core!QThread::start+0x0000000000000229)

2 个答案:

答案 0 :(得分:1)

您点击触发的帖子实际上并未尝试在线程中执行操作。您调用方法,而不是将该方法作为参数传递,因此您尝试使用test_methodNone)的返回值作为方法运行

变化:

worker = Worker(self.test_method())  # Calls test_method and tries to run None

为:

worker = Worker(self.test_method)  # Don't call test_method

答案 1 :(得分:1)

问题是您没有保存对该帖子的引用,所以一旦您退出handleButton,它就会被删除。如果你保存一个引用,那就引出了如何处理它的生命周期的问题。

QThread不仅仅是系统线程的包装器 - 它实现了其他服务,可以让您将线程连接到GUI。您可以使用其finished处理程序在窗口小部件终止时发出任何清理信号。

在此示例中,我将工作程序保存为self.worker2并阻止第二次启动工作程序,直到完成第一个工作程序。

import PyQt5
import PyQt5.QtCore as QtCore
from PyQt5.QtWidgets import *
import time

class Worker(QtCore.QThread):
    def __init__(self, method_to_run):
        super(Worker, self).__init__()
        self.method = method_to_run

    def run(self):
        self.method()

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.button = QPushButton('Test', self)
        self.label = QLabel(self)
        self.button.clicked.connect(self.handleButton)
        layout = QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self.worker = Worker(self.test_method)
        self.worker.start()
        self.worker2 = None

    def handleButton(self):
        self.label.setText('Button Clicked!')
        # likely better to disable the button instead... but
        # this shows events in action.
        if self.worker2:
            self.label.setText('Worker already running')
        else:
            self.worker2 = Worker(self.test_method)
            self.worker2.finished.connect(self.handle_worker2_done)
            self.worker2.start() 

    def handle_worker2_done(self):
        self.worker2 = None
        self.label.setText('Worker done')

    @staticmethod
    def test_method():
        #res = [i*i for i in range(100500)]
        time.sleep(3)

if __name__ == '__main__':

    import sys
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())