QThread blockt GUI bei 100% CPU last :/

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
AngelusNoctis
User
Beiträge: 92
Registriert: Sonntag 16. Dezember 2007, 20:03

Hi

Weiss jemand wie man QThread dazu bewegt die GUI (Progressbar und MainWindow) nicht zu blocken auch wenn QThread ne 100% CPU Last erzeugt?

Habs schon mit wait() und der priorität versucht aber sobald der Thread läuft und die CPU auf 100% knallt bleibt der ProgressBarDialog und das MainWindow hängen :/

Kann jemand helfen?

Code: Alles auswählen

    def onButtonApply(self):
        self.progressBar = QtGui.QProgressDialog(self, QtCore.Qt.FramelessWindowHint) #QtCore.Qt.X11BypassWindowManagerHint)
        self.progressBar.setRange(0,0)
        self.progressBar.setLabelText(self.tr('Please wait'))
        self.progressBar.setCancelButton(None)        
        self.progressBar.show()
        
        self.thread = Thread(str(self.lineDevice.text()), str(self.lineMount.text()), str(self.comboBox.currentText()), str(self.linePassphrase.text()), str(self.lineKeyfile.text()))
        self.thread.start()
        self.connect(self.thread,  QtCore.SIGNAL('finished()'), self.onFinish)

class Thread(QtCore.QThread):    
    def __init__(self, device, mountpoint, fs, passphrase, key_file):
        QtCore.QThread.__init__(self)
        self.device = device
        self.mountpoint = mountpoint
        self.fs = fs
        self.passphrase = passphrase
        self.key_file = key_file

    def run(self):
        if os.path.isfile(self.device):
            lo = addLoop(self.device)
            luksID= 'luks-' + luks_uuid(lo[1])
            luks_open(lo[1], luksID, self.passphrase, self.key_file)
            mountDevice(self.mountpoint, '/dev/mapper/' + luksID, self.fs)
        else:
            luksID = 'luks-' + luks_uuid(self.device)
            luks_open(self.device, luksID, self.passphrase, self.key_file)
            mountDevice(self.mountpoint, '/dev/mapper/' + luksID, self.fs)
lunar

Wenn die graphische Oberfläche hängen bleibt, ohne dass das ganze System abstürzt, dann ist aller Wahrscheinlichkeit nach ein Fehler in Deinem Quelltext, der dazu führt, dass der rechenintensive Abschnitt gar nicht in einem separaten Thread läuft. Ob das tatsächlich der Fall ist, und wo in diesem Fall der Fehler genau liegt, kann man Dir nicht sagen, solange Du nur Stückchen aus Deinem Programm zeigst. Du glaubst doch nicht wirklich, dass jemand mit diesem kurzen Stücken irgendeinen essentiellen Fehler in Deinem Programm aufdecken kann? Zumal in diesem Quelltext so direkt auch nichts zu erkennen ist, was die CPU tatsächlich voll auslasten könnte.

Wenn Du Hilfe möchtest, dann musst Du Dir schon die Mühe einer anständigen, detaillierten und mit möglichst kleinem, aber aussagekräftigem Quelltext hinterlegten Problembeschreibung machen.
AngelusNoctis
User
Beiträge: 92
Registriert: Sonntag 16. Dezember 2007, 20:03

Der Teil der die CPU hochjagt ist luks_open.

Die Funktion luks_open wurde von den Red Hat Devs geschrieben, ich hab den Teil 1:1 von der crypto.py aus dem Anaconda Installer.

Die Funktion nutzt python-cryptsetup das Binding zu cryptsetup was wiederum beides von den Red Hat Devs stammt.

Die C implementierung von cryptsetup jagt die CPU auch auf 100% wenn man es über die Konsole nutzt, nur dachte ich wenn das ganze in nem extra Thread (vorallem bei SMP System) läuft sollte die GUI nicht blockieren.

Sollte doch irgendwie möglich sein?

http://git.fedorahosted.org/git/?p=anac ... adf00b658f

Code: Alles auswählen

def luks_open(device, name, passphrase=None, key_file=None):
    cs = CryptSetup(yesDialog = askyes, logFunc = dolog)
    key_file_unlink = False

    if passphrase:
        key_file = cs.prepare_passphrase_file(passphrase)
        key_file_unlink = True
    elif key_file and os.path.isfile(key_file):
        pass
    else:
        raise ValueError("luks_open requires either a passphrase or a key file")

    rc = cs.luksOpen(device = device, name = name, keyfile = key_file)
    if key_file_unlink: os.unlink(key_file)
    if rc:
        raise CryptoError("luks_open failed for %s (%s)" % (device, name))
Hier noch python-cryptsetup http://git.fedorahosted.org/git/?p=pyth ... tsetup.git


Hoff das hilft weiter
BlackJack

@AngelusNoctis: Das dürfte wenig mit der CPU-Last, noch irgendwelchen Prioritäten zu tun haben. Wenn die GUI blockiert, dann hat man in der Regel etwas bei der Verwendung mit Threads falsch gemacht. In diesem Fall sieht aber (fast) alles richtig aus, also ist die Frage was genau mit "hängen" gemeint ist?

Die Reihenfolge von Thread starten und Signal verbinden ist ungünstig, weil es so passieren kann, dass der Thread fertig ist, bevor das Verbinden des Signals passiert ist.
AngelusNoctis
User
Beiträge: 92
Registriert: Sonntag 16. Dezember 2007, 20:03

@BlackJack

Das Hängen äussert sich folgender massen:

1) ProgressBar erscheint nicht sofort sondern erst nach dem "luks_open" abgeschlossen wurde
2) Nix reagiert mehr bis "luks_open" abgeschlossen wird
3) Beim verschieben des Dialoges wird der Dialog nicht neu gezeichnet und das ganze ist grau solang "luks_open" nicht abgeschlossen ist


Am signal liegts ned, aber ich werds nachher noch verschieben bzw vor den thread start.
lunar

Die Anmerkungen über die Signale hatte mit dem Problem selbst nichts zu tun, sondern war allgemeiner Natur, und sollte natürlich beachtet werden, denn BlackJack hat auch in diesem Fall recht, ich hätte das auch angemerkt, wenn es mir aufgefallen wäre.

Wie gesagt, "luks_open" läuft wahrscheinlich nicht in einem separaten Thread. Aber Du kannst testen, ob es an "luks_open" und dessen CPU-Last liegt, indem Du es durch eine blockierende Operation ohne große CPU-Last ersetzt (e.g. "time.sleep(3600)"). Blockiert die GUI dann immer noch, liegt es ganz offensichtlich nicht an "luks_open()", was uns zurück zur bereits geäußerten Vermutung führt.
AngelusNoctis
User
Beiträge: 92
Registriert: Sonntag 16. Dezember 2007, 20:03

@lunar

Also ich hab jetzt mal time.sleep(3600) reingeknallt und die GUI hängt nicht, der ProgressBar erscheint sofort...

Hab jetzt mal ein Bild zum "hängen" gemacht, man sieht schön das der ProgressBarDialog einfach nur grau ist und sich garnix tut, bis eben luks_open fertig ist...

Code: Alles auswählen

class Thread(QtCore.QThread):    
    def __init__(self, device, mountpoint, fs, passphrase, key_file):
        QtCore.QThread.__init__(self)
        self.device = device
        self.mountpoint = mountpoint
        self.fs = fs
        self.passphrase = passphrase
        self.key_file = key_file
        
    def run(self):
        time.sleep(3600)
#        if os.path.isfile(self.device):
#            lo = addLoop(self.device)
#            luksID= 'luks-' + luks_uuid(lo[1])
#            luks_open(lo[1], luksID, self.passphrase, self.key_file)
#            mountDevice(self.mountpoint, '/dev/mapper/' + luksID, self.fs)
#        else:
#            luksID = 'luks-' + luks_uuid(self.device)
#            luks_open(self.device, luksID, self.passphrase, self.key_file)
#            mountDevice(self.mountpoint, '/dev/mapper/' + luksID, self.fs)


Geht mit time.sleep(3600)

Bild

Geht ned mit luks_open

Bild




Theme Unterschiede wegen root/user...
AngelusNoctis
User
Beiträge: 92
Registriert: Sonntag 16. Dezember 2007, 20:03

Hat echt keiner ne Idee? :(

Am GIL von Python 2 kanns ned liegen? oO
lunar

@AngelusNoctis: Wenn ich eine Idee hätte, die über das bereits gesagte hinausgeht, hätte ich Dich schon nicht im Dunkeln gelassen ;)
AngelusNoctis
User
Beiträge: 92
Registriert: Sonntag 16. Dezember 2007, 20:03

@Luna k :)


Ich glaub ich schreib mal den Dialog 1:1 in C++ und guck ob der in C++ auch hängt, wenn nicht liegt es wohl an python/pyqt/pycryptsetup?
lunar

@AngelusNoctis: Es schadet bestimmt auch nicht, den Entwicklern dieses Python-Moduls diesen Fall zu schildern. Vielleicht handelt es sich ja um ein bekanntes Problem. Zudem wissen diese Entwickler am besten, wie ihr Modul funktioniert, und wo es möglicherweise mit PyQt in Konflikt treten könnte.

Lunar ;)
AngelusNoctis
User
Beiträge: 92
Registriert: Sonntag 16. Dezember 2007, 20:03

@Lunar

Stimmt, dazu müsste man aber am besten wissen obs wirklich nur unter PyQt auftritt ^^

Davon abgesehen wollte ich mal gucken ob ich pycryptsetup dazu bewegen kann das es mit python 3 läuft und die diff dann übergeben :)
Antworten