多个QThread导致PySide崩溃

0 投票
1 回答
1064 浏览
提问于 2025-04-27 13:38

我正在尝试实现一个有多个线程的程序。在主窗口的__init__方法里,我创建了这些线程。程序的界面会启动,同时线程在后台运行。问题是程序总是崩溃。不过,如果我添加或取消注释一行打印语句,程序就能正常工作。

class TestThreadingWin(QtGui.QMainWindow):

    def __init__(self,  parent=rsui.getMayaMainWindow()):
        ''' Constructor '''
        super(TestThreadingWin, self).__init__(parent)

        self.setWindowTitle('Test Threading')

        self.centralWidget = QtGui.QPlainTextEdit()
        self.setCentralWidget(self.centralWidget)

        self.centralWidget.appendPlainText("test")

        numThreads = 7
        self._threads = []

        for i in range(numThreads):
            #print self._threads  # <-- Uncomment this line, and it works?!
            testThread = QtCore.QThread()
            # Keep a reference to the thread object or it will be deleted when
            # it goes out of scope, even if it has not finished processing.
            self._threads.append(testThread)
            worker = TestThreadWorker(i)
            worker.moveToThread(testThread)

            worker.finishedProcessing.connect(self.updateStuff)
            worker.finishedProcessing.connect(testThread.quit)
            testThread.started.connect(worker.doStuff)
            testThread.finished.connect(self.deleteThread)

            testThread.start()

            QtCore.QCoreApplication.processEvents()

        print 'done creating all threads'

    def deleteThread(self):
        """ Destroy the thread object and remove the reference to it from the 
        self._threads list. """
        print 'delete thread'
        threadToDelete = self.sender()
        threadIndex = self._threads.index(threadToDelete)
        del self._threads[threadIndex]
        threadToDelete.deleteLater()

    def updateStuff(self, message):
        self.centralWidget.appendPlainText(message)

class TestThreadWorker(QtCore.QObject):
    finishedProcessing = QtCore.Signal(str)

    def __init__(self, num):
        super(TestThreadWorker, self).__init__()
        self._num = num

    def doStuff(self):
        time.sleep(1)
        choiceList = ['cat', 'bat', 'hat', 'tissue', 'paper', 'qwerty', 'mouse']
        stuff = random.choice(choiceList)
        stuff2 = '{0} {1}'.format(self._num, stuff)
        self.finishedProcessing.emit(stuff2)

def openThreadingWin():
    '''This ensures that only one instance of the UI is open at a time.'''
    global testingThreadingWin
    try:
        testingThreadingWin.close()
        testingThreadingWin.deleteLater()
    except: pass
    testingThreadingWin = TestThreadingWin()
    testingThreadingWin.show()

很奇怪,为什么一条打印语句能让程序不崩溃。我是不是漏掉了什么?

暂无标签

1 个回答

1

我终于通过使用 QThreadPool 让它工作了。这个工具会帮我管理线程,所以我不需要担心去访问那些已经被销毁的东西。主要的不同点有:

  • 现在工作类同时继承自 QtCore.QObject 和 QtCore.QRunnable。(它必须继承自 QObject 才能发出信号。)这两个父类的 __init__ 函数都必须被调用,否则程序会崩溃。
  • 不再需要额外的代码来设置连接,以确保线程在完成后会被销毁。
  • 不再需要代码来保持对线程的引用,或者在线程被销毁时删除这些引用。

这是新的代码:

class TestThreadPoolWin(QtGui.QMainWindow):
    def __init__(self,  parent=rsui.getMayaMainWindow()):
        ''' Constructor '''
        super(TestThreadPoolWin, self).__init__(parent)

        self.setWindowTitle('Test Threading')

        self.centralWidget = QtGui.QPlainTextEdit()
        self.setCentralWidget(self.centralWidget)

        self.centralWidget.appendPlainText("test")

        numThreads = 7
        threadPool = QtCore.QThreadPool.globalInstance()
        for i in range(numThreads):
            runnable = TestRunnableWorker(i)
            runnable.finishedProcessing.connect(self.updateStuff)
            threadPool.start(runnable)

        print 'done creating all threads'

    def updateStuff(self, message):
        self.centralWidget.appendPlainText(message)

class TestRunnableWorker(QtCore.QObject, QtCore.QRunnable):
    finishedProcessing = QtCore.Signal(str)

    def __init__(self, num, parent=None):
        # Be sure to run the __init__ of both parent classes, or else you will
        # get a crash.
        QtCore.QObject.__init__(self, parent)
        QtCore.QRunnable.__init__(self)

        self._num = num

    def run(self):
        time.sleep(1)
        choiceList = ['cat', 'bat', 'hat', 'tissue', 'paper', 'qwerty', 'mouse']
        stuff = random.choice(choiceList)
        stuff2 = '{0} {1}'.format(self._num, stuff)
        self.finishedProcessing.emit(stuff2)

def openThreadPoolWin():
    '''This ensures that only one instance of the UI is open at a time.'''

    global testingThreadPoolWin
    try:
        testingThreadPoolWin.close()
        testingThreadPoolWin.deleteLater()
    except: pass
    testingThreadPoolWin = TestThreadPoolWin()
    testingThreadPoolWin.show()

撰写回答