PyQt QThread:线程仍在运行时被销毁

4
尽管将对 QThread 的引用保存为 self.lightsThread,停止 QObject self.lightsWorker 后再次启动 self.lightsThread 仍会导致错误。
QThread: Destroyed while thread is still running

停止self.lightsWorker后,必须也停止QThreadself.lightsThread吗?如果不是,会出现什么问题?
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import time



class Screen(QMainWindow):
    def __init__(self):
        super(Screen, self).__init__()
        self.initUI()

    def initUI(self):
        self.lightsBtn = QPushButton('Turn On')
        self.lightsBtn.setCheckable(True)  
        self.lightsBtn.setStyleSheet("QPushButton:checked {color: white; background-color: green;}")
        self.lightsBtn.clicked.connect(self.lightsBtnHandler)

        self.setCentralWidget(self.lightsBtn)

    def lightsBtnHandler(self):
        if self.lightsBtn.isChecked():
            self.startLightsThread()
        else:
            self.stopLightsThread()

    def startLightsThread(self):
        print 'start lightsThread'
        self.lightsThread = QThread()
        self.lightsWorker = LightsWorker()
        self.lightsWorker.moveToThread(self.lightsThread)
        self.lightsThread.started.connect(self.lightsWorker.work)
        self.lightsThread.start()


    def stopLightsThread(self):
        print 'stop lightsThread'
        self.lightsWorker.stop()



class LightsWorker(QObject):
    signalFinished = pyqtSignal()

    def __init__(self):
        QObject.__init__(self)
        self._mutex = QMutex()
        self._running = True

    @pyqtSlot()
    def work(self):
        while self._running:
            print 'working'
            time.sleep(1)
        self.signalFinished.emit()

    @pyqtSlot()
    def stop(self):
        print 'Stopping'
        self._mutex.lock()
        self._running = False
        self._mutex.unlock()



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

我在我的应用程序中遇到了同样的问题。然而,这并不是真正的错误,一切似乎都在正常工作。 - Matho
2个回答

8

在停止lightWorker后,您需要退出线程并等待它停止,这是根据https://dev59.com/JWQo5IYBdhLWcg3wMs8L#32138213中的答案所述。

def stopLightsThread(self):
    print('stop lightsThread')
    self.lightsWorker.stop()
    self.lightsThread.quit()
    self.lightsThread.wait()

这个可行!.wait()是干什么用的?当不使用.wait()时,这个问题中的示例代码不会抛出错误。 - Nyxynyx
有时候在测试过程中,当我点击按钮过快时,会出现错误,因为线程尚未完成就再次尝试启动它。 - Luchko
我不需要 .wait(),但是我必须在 QThread 构造函数中添加 parent=。 - rchuso

2

我曾在C++中遇到了同样的问题,但问题是一样的。

问题在于当相关线程仍在运行时,您的QThread实例已被删除。这可能非常危险,因为线程代码执行被中断,没有任何保证该线程已准备好被删除。

例如:

  • 一个线程控制对象(工作者)的执行和生命周期
  • 资源在此对象析构函数中释放(显式或隐式,如使用QObject父/子系统时)
  • 由于线程执行被中断,对象将不会被删除

这导致内存泄漏和资源泄漏。

在您的代码中,工作者已停止,但工作线程没有停止。我不是Python专家,但似乎您的工作者对象已停止但未被删除。

要正确停止工作者和线程,您应该执行以下操作:

  • 向工作者发送消息,告诉它“停止工作”
  • 要求您的线程退出:它将向线程发布一个“退出”消息,在工作者执行后将被处理
  • 等待您的线程停止

最后一步是可选的:如果线程和工作者不与其他对象共享资源,则可能不需要等待它们完成,只需忘记它们。

唯一的例外是在退出应用程序之前,应正确停止所有线程:您应该等待所有当前正在运行的线程停止,然后再退出应用程序。

对于简单的任务,您还应考虑使用QtConcurrent框架。


谢谢,这个代码很好用,使用了Luchko发布的代码。在指定线程退出self.lightsThread.quit()后,我的代码不再抛出错误。我猜测self.lightsThread.wait()是等待线程停止,如果是这样,为什么在我的示例代码中省略它不会导致错误呢? - Nyxynyx
1
根据Qt文档,"quit"会向线程发送一个事件,告诉它停止处理。这个事件将在下一个线程的事件循环中被处理。而"wait"用于等待线程停止。 - Aurelien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接