【问题标题】:QThreads in Pyqt5: is it ok to create new objects in the constructor of the worker class?Pyqt5中的QThreads:在worker类的构造函数中创建新对象可以吗?
【发布时间】:2018-05-25 19:48:09
【问题描述】:

1。一些背景资料

关于如何实例化和使用 QThread 的官方文档可以在这里找到:http://doc.qt.io/qt-5/qthread.html

文档描述了两种基本方法:(1) 工作对象方法和 (2) QThread 子类方法。

    > 下面的文章解释了为什么在使用第二种方法时需要小心: https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

    > 以下文章解释了为什么这两种方法各有千秋: https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html

我引用:

根据经验:

  • 如果你真的不需要线程中的事件循环,你应该子类化。
  • 如果您需要一个事件循环并在线程中处理信号和槽,您可能不需要子类化。
  • 由于我确实需要在 QApplication 线程和新 QThread 之间进行某种通信(而且我相信信号槽是一种很好的通信方式),我将使用 worker-object 方法 .

     

    2。来自官方文档的示例代码

    在 QThreads 上的官方 Qt5 文档(请参阅 http://doc.qt.io/qt-5/qthread.html)上,您可以找到示例代码。我已努力将其翻译成 Python:
    (请参阅此 stackoverflow 问题以了解有关该翻译的更多详细信息:QThreads in Pyqt5: is this the correct C++ to Python translation of the official QThread docs?

    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    
    class Worker(QObject):
    
        resultReady = pyqtSignal(str)
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # Note: this constructor is empty now.
            # But would it be okay to instantiate new
            # objects here, and use them in doWork(..)?
    
        # A more general question: is it okay if
        # doWork(..) accesses variables that were
        # created in another thread (perhaps the
        # main QApplication thread)?
    
        @pyqtSlot(str)
        def doWork(self, param):
            result = "hello world"
            print("foo bar")
            # ...here is the expensive or blocking operation... #
            self.resultReady.emit(result)
    
    
    class Controller(QObject):
    
        operate = pyqtSignal(str)
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            # 1. Create 'workerThread' and 'worker' objects
            # ----------------------------------------------
            self.workerThread = QThread()
            self.worker = Worker()
            self.worker.moveToThread(self.workerThread)
    
            # 2. Connect all relevant signals
            # --------------------------------
            self.workerThread.finished.connect(self.worker.deleteLater)
            self.operate.connect(self.worker.doWork)
            self.worker.resultReady.connect(self.handleResults)
    
            # 3. Start the thread
            # --------------------
            self.workerThread.start()
    
        def __del__(self):
            self.workerThread.quit()
            self.workerThread.wait()
    
        @pyqtSlot(str)
        def handleResults(self, param):
            print(param)
            global app
            app.exit()
    
    
    if __name__ == '__main__':
        app = QCoreApplication([])
        controller = Controller()
        controller.operate.emit("foo")
        sys.exit(app.exec_())
    

    如您所见,Worker 类的构造函数是空的。这将我们带到下一段。

     

    3。我的问题:可以在worker的构造函数中创建对象吗?

    来自Mss. 的精彩文章。 Maya Posch (https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/),我引用:

    顺便说一句,这里要注意的一件非常重要的事情是,你永远不应该在 QObject 类的构造函数中分配堆对象(使用 new),因为这个分配是在主线程上执行的,而不是在新的 QThread 实例上,这意味着新创建的对象然后由主线程拥有,而不是 QThread 实例。这将使您的代码无法正常工作。相反,在这种情况下,在诸如 process() 之类的主函数槽中分配此类资源,因为当调用该对象时,该对象将位于新线程实例上,因此它将拥有该资源。
    [Maya Posch -如何真正真正地使用qthreads,完整解释]

    本文是为 C++ 软件编写的。

    这句话仍然适用于 Python 应用程序吗?也就是说,在Worker类的构造函数中实例化对象可以吗?

    更一般地说,我可以问:如果doWork(..) 函数访问在另一个线程(可能是主 QApplication 线程)中实例化的变量,是否可以?


    特此提供我当前的系统设置:
    >  Qt5 (QT_VERSION_STR = 5.10.1)
    >  PyQt5 (PYQT_VERSION_STR = 5.10.1)
    >  Python 3.6.3
    >  Windows 10,64 位

    【问题讨论】:

    • 言归正传,一个对象属于创建它的线程,在Qt的情况下,它避免了属于一个线程的对象在另一个线程中使用,所以正确的事情不是在构造函数中创建它,或者执行它然后使用 moveToThread() 将其移动到新线程。与其看语言,不如看 Qt 提供的基本概念。
    • 另外,它提供的代码与您当前的问题无关,您在构造函数中没有看到任何对象的创建,并不表示您有问题。
    • 如果您只依赖语言,将代码从一种语言翻译成另一种语言没有多大意义,因为像 Qt 这样的库提供了其他限制。
    • 嗨@eyllanesc,你是对的。 Worker 类的构造函数当前为空。我在代码中添加了注释以澄清:-)
    • 嗨@eyllanesc,您对翻译问题是正确的。请查看我的另一个 StackOverflow 问题,该问题侧重于翻译本身:stackoverflow.com/questions/50531797/…。随意张贴你的评论:-)

    标签: python python-3.x qt5 pyqt5 qthread


    【解决方案1】:

    在这个答案中,我将对我的 cmets 的所有任务进行排序。

    很多时候他把线程的概念和QThread混淆了,如果修改docs

    QThread 类提供了一种独立于平台的方式来管理线程

    它不是本地线程的包装器,但在读取时它是一个处理程序。

    run() 方法是线程的开始,docs 也表示:

    线程的起点...

    当您使用moveToThread() 时,它会更改对象及其子对象的线程亲和性,这指的是QObjects。

    对于您的引用表明,如果您仅在 doWork() 中使用 QObject,则应避免在构造函数中创建它,此外这意味着它是通常不必要的类的成员。如果你这样做的解决方案是你使用moveToThread(),万一它是Worker的孩子显然没有必要。


    Qt 为避免访问在另一个线程中创建的对象的这些问题,我建议使用线程安全的信号或 QMetaObject。

    最后QThread的使用是非常低级的,做多线程任务Qt提供了其他技术QThreadPoolQRunnable避免了这些问题。在 c++ 中也有 QtConcurrent 但它在 PyQt 中不可用

    【讨论】:

    • 嗨@eyllanesc,您确定QThread 和通常的Python 线程之间的区别吗?如果我没记错的话,你告诉我QThread 实际上是在使用预编译的 Qt 代码,因此它不会提交给 Python GIL。这导致QThread 的使用更强大,但也更危险。但是,我发现了两个与您的说法相矛盾的链接:stackoverflow.com/questions/1595649/…mail-archive.com/pyqt@riverbankcomputing.com/msg16052.html
    • @K.Mulier 那个wrapper取决于你是否严格定义它,显然QThread来处理你应该使用它们的本机线程,因此透视图是一个包装器。这些帖子的目的是让人们知道 Qt 使用本机线程并且不创建其他类型的线程。
    猜你喜欢
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    • 2012-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多