2014-05-04 47 views
3

我認爲確定當前運行函數QThread的ID是QThread.currentThreadId()。然而我發現這並沒有給出預期的結果(在PyQt5中使用python 3;但是我沒有理由相信它會和pyqt4/py 2因此不同)。線程ID以我無法解釋的方式變化,表明我無法實際使用它,其中QThread實例ID可預測地變化,表示我應該使用它來標識當前正在運行的線程。爲了測試,我創造了這個:正確使用QThread.currentThreadId()

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_()) 

當你運行它,輸入和顯示,QThread.currentThread() Python的ID是在多個地點不同,但QThread.currentThreadId()是同一事件之前循環的前4行打印的發生:

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的ID是一樣的,但是沒關係,以後的QThread的幾個實例包裝相同的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(前兩行)內的不同,這是有意義的。再次,線程ID(sip.voidptr)以不合理的方式變化:我希望第2行顯示線程ID爲0x01A4ABC0,與第一行相同,而不是與第三行(插槽)相同。

有趣的是,如果通過QtWidgets.QApplication.instance().thread()替換logthread格式線程ID輸出,你發現QThread實例ID是總是一樣的應用程序的QThread實例ID當worker.run`在單獨的線程中運行,除了。甚至在進入應用程序事件循環之前(換句話說,應用程序線程ID只有在事件循環開始後才變爲常量)。

如果我正在測試此權限,則上面指示QThread實例ID具有一致且可預測的值,一旦QApplication事件循環已啓動,但線程標識(currentThreadId())不會。因此,無論何時我想測試正在運行的函數的線程位置,我都應該使用QThread.currentThread()並可能與app.thread()比較,但我應該避免currentThreadId()。任何人看到我測試這個方式和結論的方式有什麼問題?如果沒有問題,考慮到currentThreadId()的文檔,這有什麼意義?如果我犯了一個錯誤,我做錯了什麼?

回答

4

你的問題主要源於你沒有將返回的sip.voidptr轉換爲整數的事實。如果你打印int(QThread.currentThreadId()),你會得到有意義的數字。簡而言之,你所看到的是threadId存儲位置的地址,這顯然取決於應用程序當前的內存使用情況。這些內存地址的內容總是同意。

您可能也有興趣知道Python線程模塊爲您提供了相同的一致信息(請參閱下面的示例)。

最後一件事,我感覺你的應用程序不是線程安全的,因爲你將你的self.worker對象移動到QThread,然後當你點擊「run worker here」時直接從主線程調用一個方法。在下面的例子中,爲了安全起見,我已經實例化了一個新的工作對象。

此外,請原諒你的例子轉換爲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_()) 
+0

我猜這就是人們說pyqt不像python時的意思;如果print(線程)沒有,print *(int(threadId))'會提供有用的信息。一旦事件循環開始,'print(int(threadId))'與'print(qthread)'具有相同的模式,並且您可以從任何線程獲取應用程序實例'QThread',而您只能從應用程序獲取應用程序實例threadId線程(因爲沒有'QThread.threadId()'存在,只有'QThread.currentThreadId()')。 – Schollii