Oli*_*ver 6 python multithreading pyqt qthread
我认为确定QThread当前运行函数的ID 是QThread.currentThreadId().但是我发现这没有给出预期的结果(在PyQt5中使用python 3;但是我没有理由相信它与pyqt4/py 2不同,因此通用标签).线程ID以我无法解释的方式变化,表明我实际上无法使用它,QThread实例id可预测地变化,表明我应该使用它来识别当前正在运行的线程.为了测试,我创建了这个:
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_())
Run Code Online (Sandbox Code Playgroud)
当你运行它时,前面的4行打印出现在输入事件循环之前,并显示QThread.currentThread()Python id在几个位置是不同的,但是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>
Run Code Online (Sandbox Code Playgroud)
我希望所有QThread Python id都相同,但是好的可能是QThread的几个实例包装了相同的C++线程指针.
现在单击"在此处运行worker"按钮:这只是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>
Run Code Online (Sandbox Code Playgroud)
实际上,这次QThread实例ID在所有行上都是相同的,很高兴看到.但是第3行中的线程id是不同的,由于信号输入worker.run而被插槽打印的线,但信号是在同一线程中生成的!此外,它意味着同一个QThread对象可以有几个底层线程ID.
现在单击"Start Worker".这会调用worker.run但是在worker的主题中.印刷的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>
Run Code Online (Sandbox Code Playgroud)
QThread实例ID在worker.run(前两行)内部与插槽中的实例ID不同,这是有道理的.同样,线程id(sip.voidptr)以对我没有意义的方式变化:我希望第2行显示线程ID 0x01A4ABC0,与第一行相同,而不是与第3行(槽)相同.
有趣的是,如果你用logthread格式化替换线程id输出QtWidgets.QApplication.instance().thread(),你会发现QThread实例id 总是与应用程序QThread实例id相同,除非在单独的线程中运行worker.run`.甚至在输入应用程序事件循环之前(换句话说,应用程序线程id仅在事件循环开始后才变为常量).
如果我正在测试这个权限,上面的内容表明,QThread一旦QApplication事件循环开始,实例ID具有一致且可预测的值,但线程id(currentThreadId())没有.因此,每当我想测试运行的函数的线程位置时我应该使用QThread.currentThread()并可能比较app.thread(),但我应该避免currentThreadId().有人看到我测试这个和结论的方式有任何问题吗?如果没有问题,考虑到文档,这有currentThreadId()什么意义?如果我弄错了,我做错了什么?
您的问题主要源于您没有将返回的sip.voidptr转换为整数.如果您改为打印,int(QThread.currentThreadId())则会获得有意义的数字.简而言之,您所看到的是存储threadId的内存位置的地址,这显然取决于应用程序当前的内存使用情况.这些内存地址的内容总是一致.
您可能也有兴趣知道Python线程模块为您提供相同的一致信息(请参阅下面的示例).
最后一点,我觉得你的应用程序不是线程安全的,因为你将self.worker对象移动到QThread,然后当你单击"在这里运行worker"时直接从主线程调用一个方法.在下面的示例中,我在这种情况下实例化了一个新的worker对象只是为了安全起见.
另外,请原谅您将示例转换为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_())
Run Code Online (Sandbox Code Playgroud)