В 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, она также будет выполняться в этом потоке.
Example3:
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
In thВ этом случае some_function вызывается косвенно и принадлежит контексту Worker, поэтому он будет выполняться во вторичном потоке, поэтому функция some_inner_function будет выполняться во вторичном потоке.
В заключение some_inner_function
будет выполняться в том жепоток как some_function
был выполнен, даже вызывать его прямо или косвенно, так как он не имеет контекста.