example python multithreading pyqt qthread

qthread python example



Uso apropiado de QThread.currentThreadId() (1)

Pensé que la determinación de ID para el QThread que ejecuta actualmente una función era QThread.currentThreadId() . Sin embargo, creo que esto no da los resultados esperados (en PyQt5 con python 3, pero no tengo ninguna razón para creer que sería diferente con pyqt4 / py 2, de ahí las etiquetas genéricas). El ID de la secuencia varía en formas que no puedo explicar, lo que indica que no puedo usarlo, donde la Id. De la instancia de QThread varía de manera predecible, lo que indica que debería usar eso para identificar el hilo que se está ejecutando actualmente. Para probar, creé esto:

from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import pyqtSignal import time import sys def logthread(caller): print(''%-25s: %s, %s'' % (caller, QtCore.QThread.currentThread(), QtCore.QThread.currentThreadId())) class Worker(QtCore.QObject): done = pyqtSignal() def __init__(self, parent=None): logthread(''worker.__init__'') super().__init__(parent) def run(self, m=10): logthread(''worker.run'') for x in range(m): y = x + 2 time.sleep(0.001) logthread(''worker.run finished'') self.done.emit() class MainWindow(QtWidgets.QWidget): def __init__(self, parent=None): logthread(''mainwin.__init__'') super().__init__(parent) self.worker = Worker() self.workerThread = None self.btn = QtWidgets.QPushButton(''Start worker in thread'') self.btn2 = QtWidgets.QPushButton(''Run worker here'') layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.btn) layout.addWidget(self.btn2) self.run() def run(self): logthread(''mainwin.run'') self.workerThread = QtCore.QThread() self.worker.moveToThread(self.workerThread) self.worker.done.connect(self.workerDone) self.btn.clicked.connect(self.worker.run) self.btn2.clicked.connect(self.runWorkerHere) self.workerThread.start() self.show() def workerDone(self): logthread(''mainwin.workerDone'') def runWorkerHere(self): logthread(''mainwin.runWorkerHere'') self.worker.run() if __name__ == ''__main__'': app = QtWidgets.QApplication([]) logthread(''main'') window = MainWindow() sys.exit(app.exec_())

Cuando lo ejecuta, las primeras 4 líneas impresas se producen antes de que se ingrese el bucle de evento y muestra que el QThread.currentThread() Python QThread.currentThread() es diferente en varias ubicaciones, pero el QThread.currentThreadId() es el mismo:

main : <PyQt5.QtCore.QThread object at 0x01ABDD00>, <sip.voidptr object at 0x01A4ABC0> mainwin.__init__ : <PyQt5.QtCore.QThread object at 0x01ABDD50>, <sip.voidptr object at 0x01A4ABC0> worker.__init__ : <PyQt5.QtCore.QThread object at 0x01ABDDA0>, <sip.voidptr object at 0x01A4ABC0> mainwin.run : <PyQt5.QtCore.QThread object at 0x01ABDE90>, <sip.voidptr object at 0x01A4ABC0>

Esperé que todos los Id de QThread Python fueran iguales, pero vale que varias instancias de QThread envuelvan el mismo puntero de hilo de C ++.

Ahora haga clic en el botón "Ejecutar trabajador aquí": esto simplemente llama al método worker.run directamente desde el hilo de GUI, por lo que el método debe indicar que se está ejecutando en ese hilo. Esto imprime estas cuatro líneas:

mainwin.runWorkerHere : <PyQt5.QtCore.QThread object at 0x01ABDEE0>, <sip.voidptr object at 0x01A4ABC0> worker.run : <PyQt5.QtCore.QThread object at 0x01ABDEE0>, <sip.voidptr object at 0x01A4ABC0> worker.run finished : <PyQt5.QtCore.QThread object at 0x01ABDEE0>, <sip.voidptr object at 0x01A4ACC8> mainwin.workerDone : <PyQt5.QtCore.QThread object at 0x01ABDEE0>, <sip.voidptr object at 0x01A4ABC0>

De hecho, esta vez, la QThread instancia de QThread es igual en todas las líneas, es agradable de ver. ¡Pero la identificación del hilo es diferente en la 3ra línea, la línea que se imprime en la ranura como resultado de la señal en worker.run , pero la señal se generó en el mismo hilo! Además, significa que el mismo objeto QThread puede tener varias identificaciones de subproceso subyacentes.

Ahora haz clic en "Iniciar trabajador". Esto llama a worker.run pero en el hilo del trabajador. Las 3 líneas impresas son:

worker.run : <PyQt5.QtCore.QThread object at 0x01ABDE90>, <sip.voidptr object at 0x01A4ABC0> worker.run finished : <PyQt5.QtCore.QThread object at 0x01ABDE90>, <sip.voidptr object at 0x01A4ACC8> mainwin.workerDone : <PyQt5.QtCore.QThread object at 0x01ABDEE0>, <sip.voidptr object at 0x01A4ACC8>

La QThread instancia de QThread es diferente dentro de la worker.run (primeras dos líneas) que en la ranura, esto tiene sentido. De nuevo, el id. sip.voidptr subproceso ( sip.voidptr ) varía en formas que no tienen sentido para mí: esperaba que la línea 2 mostrara la id. De subproceso 0x01A4ABC0, lo mismo que la primera línea, en lugar de la 3ª línea (la ranura).

Curiosamente, si reemplaza la salida de Id. De subproceso en formato logthread por QtWidgets.QApplication.instance().thread() , encontrará que el QThread instancia de QThread es siempre el mismo que el Id. De instancia de QThread de la aplicación, excepto cuando se ejecuta el worker.run` en el hilo separado. Incluso antes de que se ingrese el bucle de evento de la aplicación (en otras palabras, el id. Del subproceso de la aplicación se vuelve constante solo después de que se inicie el bucle de evento).

Si estoy probando este derecho, lo anterior indica que el QThread instancia de QThread tiene un valor constante y predecible una vez que se ha iniciado el QApplication evento QApplication , pero que el id. Del subproceso ( currentThreadId() ) no lo hace. Por lo tanto, cada vez que quiero probar la ubicación de un hilo de una función en ejecución, debería usar QThread.currentThread() y posiblemente comparar con app.thread() , pero debería evitar currentThreadId() . ¿Alguien ve algún problema con la forma en que probé esto y la conclusión? Si no hay problemas, ¿cómo tiene sentido esto, dados los documentos para currentThreadId() ? Si cometí un error, ¿qué hice mal?


Su problema se debe principalmente al hecho de que no está convirtiendo el sip.voidptr devuelto en un número entero. Si en su lugar imprime int(QThread.currentThreadId()) obtienes números significativos. En resumen, lo que estabas viendo era la dirección de la ubicación de la memoria donde se almacenaba threadId, lo que obviamente depende del uso actual de la memoria de las aplicaciones. El contenido de esas direcciones de memoria siempre está de acuerdo.

También le puede interesar saber que el módulo de subprocesamiento de Python le brinda la misma información coherente (consulte el ejemplo a continuación).

Una última cosa, siento que su aplicación no es segura para subprocesos porque mueve su objeto self.worker a un QThread, y luego llama directamente a un método desde el hilo principal cuando hace clic en "ejecutar trabajador aquí". En mi ejemplo a continuación, he instanciado un nuevo objeto de trabajo en ese caso solo para estar seguro.

¡Además, perdone esta conversión de su ejemplo a PyQt4 y Python 2.7!

from PyQt4 import QtCore, QtGui from PyQt4.QtCore import pyqtSignal import time import sys import threading def logthread(caller): print(''%-25s: %s, %s,'' % (caller, QtCore.QThread.currentThread(), int(QtCore.QThread.currentThreadId()))) print(''%-25s: %s, %s,'' % (caller, threading.current_thread().name, threading.current_thread().ident)) class Worker(QtCore.QObject): done = pyqtSignal() def __init__(self, parent=None): logthread(''worker.__init__'') super(Worker, self).__init__(parent) def run(self, m=10): logthread(''worker.run'') for x in range(m): y = x + 2 time.sleep(0.001) logthread(''worker.run finished'') self.done.emit() class MainWindow(QtGui.QWidget): def __init__(self, parent=None): logthread(''mainwin.__init__'') super(MainWindow, self).__init__(parent) self.worker = Worker() self.workerThread = None self.btn = QtGui.QPushButton(''Start worker in thread'') self.btn2 = QtGui.QPushButton(''Run worker here'') layout = QtGui.QVBoxLayout(self) layout.addWidget(self.btn) layout.addWidget(self.btn2) self.run() def run(self): logthread(''mainwin.run'') self.workerThread = QtCore.QThread() self.worker.moveToThread(self.workerThread) self.worker.done.connect(self.workerDone) self.btn.clicked.connect(self.worker.run) self.btn2.clicked.connect(self.runWorkerHere) self.workerThread.start() self.show() def workerDone(self): logthread(''mainwin.workerDone'') def runWorkerHere(self): logthread(''mainwin.runWorkerHere'') worker = Worker() worker.done.connect(self.workerDone) worker.run() # self.worker.run() if __name__ == ''__main__'': app = QtGui.QApplication([]) logthread(''main'') window = MainWindow() sys.exit(app.exec_())