C++ moveToThread vs从Qt中的QThread派生

C++ moveToThread vs从Qt中的QThread派生,c++,multithreading,qt,C++,Multithreading,Qt,什么时候应该首选moveToThread而不是子类化QThread 这表明这两种方法都有效。我应该在什么基础上决定从这两个方面使用什么?作为起点:两者都不使用。在大多数情况下,您有一个希望异步运行的工作单元。用这个 如果您有一个对事件做出反应和/或使用计时器的对象,那么它是一个QObject,应该是非阻塞的,并进入线程,可能与其他对象共享 这样的对象还可以包装阻塞API 子类化QThread在实践中从来都不是必需的。这就像子类化QFileQThread是一个线程句柄。它包装了一个系统资源。重载它

什么时候应该首选
moveToThread
而不是子类化
QThread


这表明这两种方法都有效。我应该在什么基础上决定从这两个方面使用什么?

作为起点:两者都不使用。在大多数情况下,您有一个希望异步运行的工作单元。用这个

如果您有一个对事件做出反应和/或使用计时器的对象,那么它是一个
QObject
,应该是非阻塞的,并进入线程,可能与其他对象共享

这样的对象还可以包装阻塞API


子类化
QThread
在实践中从来都不是必需的。这就像子类化
QFile
QThread
是一个线程句柄。它包装了一个系统资源。重载它有点傻。

QThread是低级线程抽象,首先看看高级API模块和

如果这些都不适合您,请阅读,它告诉您应该如何使用QThread。将此线程中执行的线程和任务视为单独的对象,不要将它们混合在一起

所以,如果您需要编写定制的、特定的或扩展的线程包装器,那么您应该将QThread子类化

如果您有带有信号和插槽的QObject派生类,则在其上使用moveToThread

在其他情况下,请使用QtConcurrent、QRunnable和QThreadPoll。

简单答案始终有效。 将对象移动到线程时:

  • 编写测试代码很容易
  • 重构代码很容易(可以使用线程,但不必)
  • 您不能将线程的功能与业务逻辑混用
  • 对象生存期没有问题
当您子类化
QThread

  • 编写测试代码比较困难
  • 对象清理过程可能会变得非常混乱,导致奇怪的错误
Qt博客中有该问题的完整描述:

QtConcurrent::run
也非常方便


请记住,默认情况下,当从其他线程对象发送信号时,插槽尝试在踏板之间跳转。有关详细信息,请参阅。

的文档。我将重点介绍这两种方法之间的差异。没有一个通用的答案可以适用于所有的用例,因此最好确切地了解它们是什么,以选择最适合您的用例

使用moveToThread() moveToThread()用于控制对象的线程亲缘关系,这基本上意味着设置线程(或者更好地设置Qt事件循环),对象将从该线程发出信号并执行其插槽

如您链接的文档所示,这可以用于在不同的线程上运行代码,基本上创建一个虚拟工作线程,将代码写入一个公共插槽(在示例中为doWork()插槽),然后使用moveToThread将其移动到不同的事件循环

然后,连接到该插槽的信号被触发。由于发出信号的对象(示例中的控制器)位于不同的线程中,并且该信号通过排队连接连接到我们的doWork方法,因此doWork方法将在工作线程中执行

这里的关键是您正在创建一个由工作线程运行的新事件循环。因此,一旦doWork槽启动,整个事件循环将一直忙到退出,这意味着传入信号将排队

子类化QThread() Qt文档中描述的另一种方法是将QThread子类化。在本例中,可以重写创建事件循环的QThread::run()方法的默认实现来运行其他内容

这种方法本身并没有什么问题,尽管有几个缺点

首先,编写不安全的代码非常容易,因为run()方法是该类中唯一一个实际在另一个线程上运行的方法

例如,如果您有一个在构造函数中初始化然后在run()方法中使用的成员变量,那么您的成员将在调用方的线程中初始化,然后在新线程中使用

对于任何可以从调用者或内部run()调用的公共方法,情况都是一样的

此外,插槽将从调用方的线程执行(除非您执行了一些非常奇怪的操作,如moveToThread(这个)),从而导致额外的混乱

所以,这是可能的,但你真的是靠你自己的方法,你必须格外注意

其他办法 当然,这两种方法都有不同的选择,这取决于您需要什么。如果您只需要在后台运行GUI线程时运行一些代码,可以考虑使用./P> 但是,请记住,QtConcurrent将使用全局QThreadPool。如果整个池都很忙(意味着池中没有可用的线程),代码将不会立即运行


另一种选择是,如果您至少在C++11上,则使用较低级别的API,例如。

子类化QThread在实践中是不必要的。
如果我不创建QThread的子类,那么重新定义run()函数的方法是什么?您没有提到“movetothread”。在什么情况下应该优先考虑?子类化qthread和movetothread都需要编写一个新类。移动到线程的对象可能会有很多功能。无论如何,所有这些都将属于一个单独的类。如果没有,则编写一个独立函数或lambda提交到
QtConcurrent::run
。子类化
QThread
从来都不是一个解决方案,因为它绝对是一个奇怪的对象。它位于错误的线程中。它并不意味着在向其添加插槽时被子类化,等等。@Aquarius_Girl moveToThread用于确定QObject线程亲和力。如果您有发出信号和接收插槽的类,它们应该在不同的线程中工作,那么