正确使用QThread.currentThreadId()

2024-09-28 22:23:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我认为确定当前运行函数的QThread的ID是QThread.currentThreadId()。但是,我发现这并没有给出预期的结果(在python 3的PyQt5中;但是我没有理由相信它会与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_())

当您运行它时,打印的前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>
<>我希望所有的QPythPython ID都是相同的,但是OK可能是QTrx包装同一个C++线程指针的几个例子。

现在单击“Run worker here”按钮:这只是直接从GUI线程调用worker.run方法,因此该方法应该指示它正在该线程中运行。这将打印这四行:

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在所有行上都是相同的,很高兴看到。但是第三行的线程id是不同的,这一行是由插槽在worker.run中的信号打印出来的,但是信号是在同一个线程中生成的!同样,这意味着同一个QThread对象可以有几个底层线程id

现在点击“开始工作”。这调用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实例ID在worker.run(前两行)中与在slot中不同,这是有意义的。同样,线程id(sip.voidptr)的变化方式对我来说毫无意义:我希望第2行显示线程id 0x01A4ABC0,与第一行相同,而不是与第三行(插槽)相同。

有趣的是,如果将logthread格式的线程id输出替换为QtWidgets.QApplication.instance().thread(),则会发现QThread实例id始终与应用程序QThread实例id相同,除非worker.run`在单独的线程中运行。甚至在进入应用程序事件循环之前(换句话说,只有在事件循环开始之后,应用程序线程id才变为常量)。

如果我测试的是正确的,那么上面的说明一旦启动了QApplication事件循环,QThread实例ID就具有一致且可预测的值,但是线程ID(currentThreadId())没有。因此,每当我想测试运行的函数的线程位置时,我应该使用QThread.currentThread(),并可能与app.thread()进行比较,但是我应该避免currentThreadId()。有人看到我测试这个和结论的方式有什么问题吗?如果没有问题,那么考虑到currentThreadId()的文档,这有什么意义?如果我做错了,我做错了什么?


Tags: runselfidobjectinit线程pyqt5at
1条回答
网友
1楼 · 发布于 2024-09-28 22:23:06

您的问题主要是因为您没有将返回的sip.voidptr转换为整数。如果改为打印int(QThread.currentThreadId()),则会得到有意义的数字。简而言之,您看到的是存储threadId的内存位置的地址,这显然取决于应用程序当前的内存使用情况。但是这些内存地址的内容总是一致的。

您可能还想知道Python线程模块为您提供了相同的一致信息(参见下面的示例)。

最后一件事,我觉得你的应用程序不是线程安全的,因为你将self.worker对象移动到一个QThread,然后当你点击“Runworkerhere”时直接从主线程调用一个方法。在下面的例子中,为了安全起见,我实例化了一个新的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_())

相关问题 更多 >