PySide / PyQt - Beim Starten eines CPU-intensiven Threads bleibt die gesamte Anwendung hängen

Ich versuche, in meiner PySide-GUI-Anwendung eine ziemlich häufige Aufgabe auszuführen: Ich möchte eine CPU-intensive Aufgabe an einen Hintergrund-Thread delegieren, damit meine GUI nicht reagiert und während der Berechnung sogar eine Fortschrittsanzeige anzeigt.

Folgendes mache ich (ich verwende PySide 1.1.1 unter Python 2.7, Linux x86_64):

import sys
import time
from PySide.QtGui import QMainWindow, QPushButton, QApplication, QWidget
from PySide.QtCore import QThread, QObject, Signal, Slot

class Worker(QObject):
    done_signal = Signal()

    def __init__(self, parent = None):
        QObject.__init__(self, parent)

    @Slot()
    def do_stuff(self):
        print "[thread %x] computation started" % self.thread().currentThreadId()
        for i in range(30):
            # time.sleep(0.2)
            x = 1000000
            y = 100**x
        print "[thread %x] computation ended" % self.thread().currentThreadId()
        self.done_signal.emit()


class Example(QWidget):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

        self.work_thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.work_thread)
        self.work_thread.started.connect(self.worker.do_stuff)
        self.worker.done_signal.connect(self.work_done)

    def initUI(self):

        self.btn = QPushButton('Do stuff', self)
        self.btn.resize(self.btn.sizeHint())
        self.btn.move(50, 50)       
        self.btn.clicked.connect(self.execute_thread)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Test')    
        self.show()


    def execute_thread(self):
        self.btn.setEnabled(False)
        self.btn.setText('Waiting...')
        self.work_thread.start()
        print "[main %x] started" % (self.thread().currentThreadId())

    def work_done(self):
        self.btn.setText('Do stuff')
        self.btn.setEnabled(True)
        self.work_thread.exit()
        print "[main %x] ended" % (self.thread().currentThreadId())


def main():

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Die Anwendung zeigt ein einzelnes Fenster mit einer Schaltfläche an. Wenn die Taste gedrückt wird, wird sie sich voraussichtlich selbst deaktivieren, während die Berechnung ausgeführt wird. Dann sollte die Schaltfläche wieder aktiviert werden.

Stattdessen friert das gesamte Fenster ein, wenn ich die Taste drücke, während die Berechnung ausgeführt wird, und wenn es abgeschlossen ist, erhalte ich die Kontrolle über die Anwendung zurück. Die Schaltfläche scheint niemals deaktiviert zu sein. Eine lustige Sache, die mir aufgefallen ist, ist, dass ich beim Ersetzen der CPU intensive Berechnungen vornehmedo_stuff() Mit einem einfachen time.sleep () verhält sich das Programm wie erwartet.

Ich weiß nicht genau, was los ist, aber es sieht so aus, als ob die Priorität des zweiten Threads so hoch ist, dass der GUI-Thread tatsächlich nicht mehr geplant werden kann. Wenn der zweite Thread in den Zustand BLOCKED geht (wie es bei a passiert)sleep()) hat die GUI tatsächlich die Möglichkeit, die Benutzeroberfläche wie erwartet auszuführen und zu aktualisieren. Ich habe versucht, die Priorität des Worker-Threads zu ändern, aber es sieht so aus, als ob dies unter Linux nicht möglich ist.

Ich versuche auch, die Thread-IDs zu drucken, bin mir aber nicht sicher, ob ich es richtig mache. Wenn ja, scheint die Thread-Affinität korrekt zu sein.

Ich habe auch versucht, das Programm mit PyQt und das Verhalten ist genau das gleiche, daher die Tags und Titel. Wenn ich es mit PyQt4 anstelle von PySide zum Laufen bringen kann, kann ich meine gesamte Anwendung auf PyQt4 umstellen

Antworten auf die Frage(2)

Ihre Antwort auf die Frage