3

1. 介绍

我正在使用PyQt5Python 3.7 开发一个多线程应用程序,为此我依赖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. 那是因为:

  1. 我已将Worker()对象推到my_thread.
  2. 当命令my_thread开始时,我实际上已经在那个线程中产生了一个新的 Qt 事件循环。my_worker对象存在于这个事件循环中。它的所有插槽都可以接收一个事件,该事件在此事件循环中执行。
  3. some_function()被正确注释为@pyqtSlot(). 单次计时器挂钩到此插槽并触发一个事件。多亏了 Qt-event-loop in my_thread,插槽有效地执行了它的代码my_thread

 

2. 我的问题

我的问题是关于嵌套函数(也称为“内部函数”)。考虑一下:

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()-object 所在的线程中运行?

 

3.旁注:如何hook到内部函数

您可能想知道我如何将某些东西与内部函数挂钩。好吧,考虑以下几点:

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()-object 已分配给my_thread)?

4

1 回答 1

2

在 Qt 中有以下关于什么的规则:

  1. 如果您直接调用可调用对象,它将在调用它的线程上运行。

  2. 如果一个可调用对象被间接调用(通过 qt 信号QTimer::singleShot()QMetaObject::invokeMethod()),它将在它所属的上下文中执行。而上下文是指QObject。

  3. 如果可调用对象不属于上下文,则将在间接调用它的线程中执行。

  4. 内部函数不属于上下文,因此即使直接或间接调用,也会在调用它的线程中执行。

基于以上,我们来分析几个案例作为练习来验证之前的规则:

示例 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,甚至直接或间接调用它,因为它没有上下文。

于 2019-05-11T21:22:02.957 回答