我正在多线程应用程序上使用Python 3.7中的PyQt5
,而我依赖于QThread
。
现在假设我有一个从QObject
派生的类。在该类中,我定义了一个用@pyqtSlot
注释的函数:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import threading
...
class Worker(QObject):
def __init__(self):
super().__init__()
return
@pyqtSlot()
def some_function(self):
...
return
在其他一些代码中,我实例化Worker()
并将其移至新线程,如下所示:
my_thread = QThread()
my_worker = Worker()
my_worker.moveToThread(my_thread)
my_thread.start()
QTimer.singleShot(100, my_worker.some_function)
return
通常,some_function()
现在应该在my_thread
中运行。那是因为:
Worker()
对象推到my_thread
。my_thread
启动时,我实际上已经在该线程中诞生了一个新的Qt事件循环。 my_worker
对象存在在此事件循环中。它的所有插槽都可以接收一个事件,该事件将在此事件循环中执行。some_function()
已正确注释为@pyqtSlot()
。单拍定时器挂接到此插槽并触发事件。感谢my_thread
中的Qt-event-loop,该插槽有效地执行了my_thread
中的代码。
我的问题是关于嵌套函数(也称为“内部函数”)的。考虑一下:
class Worker(QObject):
def __init__(self):
super().__init__()
return
def some_function(self):
...
@pyqtSlot()
def some_inner_function():
...
return
return
如您所见,some_inner_function()
被注释为@pyqtSlot
。它的代码还会在Worker()
对象所在的线程中运行吗?
您可能想知道如何将某些东西连接到内部函数。好吧,请考虑以下几点:
class Worker(QObject):
def __init__(self):
super().__init__()
return
def some_function(self):
@pyqtSlot()
def some_inner_function():
# Will this code run in `my_thread`?
...
return
# some_function() will run in the main thread if
# it is called directly from the main thread.
QTimer.singleShot(100, some_inner_function)
return
如果直接从主线程调用some_function()
,它将(不幸地)在主线程中运行。如果没有正确使用信号插槽机制,您将无法切换线程。
some_function()
内的单拍定时器挂在some_inner_function()
上并触发。内部函数是否将在my_thread
中执行(假设Worker()
对象已分配给my_thread
)?
答案 0 :(得分:2)
在Qt中,有关以下内容的规则如下:
如果直接调用可调用对象,它将在调用它的线程上运行。
如果可调用对象是间接调用的(通过qt信号QTimer::singleShot()
或QMetaObject::invokeMethod()
),它将在它所属的上下文中执行。上下文是指QObject。
如果可调用对象不属于上下文,它将在间接调用它的线程中执行。
内部函数不属于上下文,因此,即使直接或间接调用它,它也将在调用它的线程中执行。
基于上述内容,让我们分析几种情况以验证以前的规则:
示例1
from PyQt5 import QtCore
import threading
class Worker(QtCore.QObject):
def some_function(self):
def some_inner_function():
print("inner thread", threading.get_ident())
QtCore.QThread.sleep(1)
print("worker thread", threading.get_ident())
some_inner_function()
if __name__ == "__main__":
import sys
app = QtCore.QCoreApplication(sys.argv)
thread = QtCore.QThread()
thread.start()
my_worker = Worker()
my_worker.moveToThread(thread)
my_worker.some_function()
print("main thread", threading.get_ident())
sys.exit(app.exec_())
输出:
worker thread 140678349403776
inner thread 140678349403776
main thread 140678349403776
在这种情况下,因为直接调用了所有可调用对象,所以满足了规则1。
示例2
from PyQt5 import QtCore
import threading
class Worker(QtCore.QObject):
def some_function(self):
@QtCore.pyqtSlot()
def some_inner_function():
print("inner thread", threading.get_ident())
QtCore.QThread.sleep(1)
print("worker thread", threading.get_ident())
QtCore.QTimer.singleShot(0, some_inner_function)
if __name__ == "__main__":
import sys
app = QtCore.QCoreApplication(sys.argv)
thread = QtCore.QThread()
thread.start()
my_worker = Worker()
my_worker.moveToThread(thread)
my_worker.some_function()
print("main thread", threading.get_ident())
sys.exit(app.exec_())
输出:
worker thread 139721158932096
main thread 139721158932096
inner thread 139721158932096
在这种情况下,某些函数直接在主线程中调用,因此它将在该线程中执行,并且由于some_inner_function由some_function调用,因此它也将在该线程中执行。
示例3:
from PyQt5 import QtCore
import threading
class Worker(QtCore.QObject):
def some_function(self):
@QtCore.pyqtSlot()
def some_inner_function():
print("inner thread", threading.get_ident())
QtCore.QThread.sleep(1)
print("worker thread", threading.get_ident())
QtCore.QTimer.singleShot(0, some_inner_function)
if __name__ == "__main__":
import sys
app = QtCore.QCoreApplication(sys.argv)
thread = QtCore.QThread()
thread.start()
my_worker = Worker()
my_worker.moveToThread(thread)
QtCore.QTimer.singleShot(0, my_worker.some_function)
print("main thread", threading.get_ident())
sys.exit(app.exec_())
输出:
main thread 139934436517504
worker thread 139934378075904
inner thread 139934378075904
在这种情况下,some_function被间接调用,并且属于Worker上下文,因此它将在辅助线程上执行,因此some_inner_function将在辅助线程上执行。
最后,some_inner_function
将与执行some_function
的线程一起运行,甚至由于没有上下文而直接或间接调用它。