Правильное использование QThread.currentThreadId()
Я думал, что определение идентификатора для QThread
в настоящее время работает функция была QThread.currentThreadId()
, Однако я считаю, что это не дает ожидаемых результатов (в PyQt5 с python 3; но у меня нет оснований полагать, что это будет иначе с pyqt4 / py 2, следовательно, с общими тегами). Идентификатор потока варьируется способами, которые я не могу объяснить, что указывает на то, что я не могу его использовать, где идентификатор экземпляра QThread изменяется предсказуемо, указывая на то, что я должен использовать это для идентификации текущего запущенного потока. Чтобы проверить, я создал это:
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_())
Когда вы запускаете его, первые 4 напечатанные строки появляются перед тем, как вводится цикл обработки событий, и показывают, что QThread.currentThread()
Идентификатор Python отличается в нескольких местах, но QThread.currentThreadId()
такой же:
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>
Я ожидал, что все идентификаторы QThread Python будут одинаковыми, но, может быть, несколько экземпляров QThread обернут один и тот же указатель потока C++.
Теперь нажмите кнопку "Выполнить рабочий здесь": это просто вызывает worker.run
метод непосредственно из потока GUI, поэтому метод должен указывать, что он работает в этом потоке. Это печатает эти четыре строки:
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>
Действительно, на этот раз QThread
ID экземпляра одинаков во всех строках, приятно видеть. Но идентификатор потока отличается в 3-й строке, строке, которая печатается в слоте в результате сигнала в worker.run
, но сигнал был сгенерирован в той же теме! Кроме того, это означает, что то же самое QThread
У объекта может быть несколько базовых идентификаторов потоков.
Теперь нажмите "Начать работу". Это звонки worker.run
но в теме рабочего. 3 напечатанные строки:
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>
QThread
Идентификатор экземпляра отличается внутри worker.run
(первые две строки), чем в слоте, это имеет смысл. Опять нить id (sip.voidptr
) меняются способами, которые не имеют смысла для меня: я ожидал, что в строке 2 будет показан идентификатор потока 0x01A4ABC0, такой же, как в первой строке, а не как в третьей строке (слоте).
Интересно, что если вы замените вывод идентификатора потока в logthread
отформатировать QtWidgets.QApplication.instance().thread()
, вы обнаружите, что QThread
Идентификатор экземпляра всегда совпадает с идентификатором экземпляра приложения QThread, за исключением случаев, когда worker.run` запускается в отдельном потоке. Даже до ввода цикла событий приложения (другими словами, идентификатор потока приложения становится постоянным только после запуска цикла событий).
Если я проверяю это право, приведенные выше QThread
ID экземпляра имеет постоянное и предсказуемое значение, как только QApplication
цикл обработки событий начался, но этот идентификатор потока (currentThreadId()
) не. Следовательно, всякий раз, когда я хочу проверить расположение потока работающей функции, я должен использовать QThread.currentThread()
и, возможно, сравнить с app.thread()
, но я должен избегать currentThreadId()
, Кто-нибудь видит какие-либо проблемы с тем, как я проверил это и заключение? Если нет проблем, как это имеет смысл, учитывая документы для currentThreadId()
? Если я ошибся, что я сделал не так?
1 ответ
Ваша проблема в основном связана с тем, что вы не конвертируете возвращенный sip.voidptr в целое число. Если вместо этого вы печатаете int(QThread.currentThreadId())
Вы получаете значимые цифры. Короче говоря, вы просматривали адрес ячейки памяти, где хранился threadId, который, очевидно, зависит от текущего использования памяти приложениями. Содержание этих адресов памяти всегда совпадает, хотя.
Вам также может быть интересно узнать, что модуль потоков Python предоставляет вам такую же непротиворечивую информацию (см. Пример ниже).
И последнее, я чувствую, что ваше приложение небезопасно, потому что вы перемещаете self.worker
объект QThread, а затем непосредственно вызывать метод из основного потока, когда вы нажимаете "запустить работника здесь". В моем примере ниже я создал новый рабочий объект в этом случае, чтобы быть в безопасности.
Также, пожалуйста, прости это преобразование твоего примера в PyQt4 и 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_())