Pyqt5中的QThreads:在worker类的构造函数中创建新对象可以吗?
Posted
技术标签:
【中文标题】Pyqt5中的QThreads:在worker类的构造函数中创建新对象可以吗?【英文标题】:QThreads in Pyqt5: is it ok to create new objects in the constructor of the worker class? 【发布时间】: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:(请参阅此 *** 问题以了解有关该翻译的更多详细信息: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,您对翻译问题是正确的。请查看我的另一个 *** 问题,该问题侧重于翻译本身:***.com/questions/50531797/…。随意张贴你的评论:-)
【参考方案1】:
在这个答案中,我将对我的 cmets 的所有任务进行排序。
很多时候他把线程的概念和QThread混淆了,如果修改docs:
QThread 类提供了一种独立于平台的方式来管理线程
它不是本地线程的包装器,但在读取时它是一个处理程序。
run()
方法是线程的开始,docs 也表示:
线程的起点...
当您使用moveToThread()
时,它会更改对象及其子对象的线程亲和性,这指的是QObject
s。
对于您的引用表明,如果您仅在 doWork()
中使用 QObject
,则应避免在构造函数中创建它,此外这意味着它是通常不必要的类的成员。如果你这样做的解决方案是你使用moveToThread()
,万一它是Worker的孩子显然没有必要。
Qt 为避免访问在另一个线程中创建的对象的这些问题,我建议使用线程安全的信号或 QMetaObject。
最后QThread
的使用是非常低级的,做多线程任务Qt提供了其他技术QThreadPool
和QRunnable
避免了这些问题。在 c++ 中也有 QtConcurrent
但它在 PyQt 中不可用
【讨论】:
嗨@eyllanesc,您确定QThread
和通常的Python 线程之间的区别吗?如果我没记错的话,你告诉我QThread
实际上是在使用预编译的 Qt 代码,因此它不会提交给 Python GIL。这导致QThread
的使用更强大,但也更危险。但是,我发现了两个与您的说法相矛盾的链接:***.com/questions/1595649/… 和 mail-archive.com/pyqt@riverbankcomputing.com/msg16052.html
@K.Mulier 那个wrapper取决于你是否严格定义它,显然QThread来处理你应该使用它们的本机线程,因此透视图是一个包装器。这些帖子的目的是让人们知道 Qt 使用本机线程并且不创建其他类型的线程。以上是关于Pyqt5中的QThreads:在worker类的构造函数中创建新对象可以吗?的主要内容,如果未能解决你的问题,请参考以下文章
一起使用 psycopg2 和 Qthreads(或者只是 postgresql 和 qthreads)并更新 GUI