moveToThread vs从Qt中的QThread派生

Aqu*_*irl 23 c++ qt multithreading

什么时候应该moveToThread优先于子类QThread

此链接显示两种方法都有效.我应该在什么基础上决定从这两者中使用什么?

Ser*_*one 17

我会专注于两种方法之间的差异.没有适合所有用例的一般答案,因此最好准确了解它们是什么来选择最适合您的情况.

使用moveToThread()

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

当你链接的文档中所示,这可以被用于在不同的线程中运行代码,基本上创建虚设工人,编写代码在运行公共时隙(在该示例中的doWork()时隙),然后使用moveToThread到将其移动到不同的事件循环.

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

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

子类化QThread()

Qt文档中描述的另一种方法是继承QThread.在这种情况下,一个覆盖QThread :: run()方法的默认实现,该方法创建一个事件循环,以运行其他东西.

这种方法本身没有任何问题,尽管有几种捕获方式.

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

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

任何可以从调用者或内部run()调用的公共方法都有相同的故事.

此外,还会从调用者的线程执行插槽(除非你做了像moveToThread(this)那样非常奇怪的事情),导致额外的混乱.

所以,这是有可能的,但你真的是靠这种方法自己,你必须特别注意.

其他方法

根据您的需要,这两种方法当然都有替代方案.如果你只需要在GUI线程运行时在后台运行一些代码,你可以考虑使用QtConcurrent :: run().

但是,请记住,QtConcurrent将使用全局QThreadPool.如果整个池忙(意味着池中没有可用的线程),则代码将不会立即运行.

另一种选择,如果你至少在C++ 11上,则使用较低级别的API,例如std :: thread.


Rei*_*ica 8

作为一个起点:不要使用.在大多数情况下,您有一个工作单元,您希望以异步方式运行.使用QtConcurrent::run了点.

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

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

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


Mar*_*k R 5

简单的答案总是。当您将对象移动到线程时:

  • 为代码编写测试很容易
  • 重构代码很容易(您可以使用线程,但不是必须的)。
  • 您不会将线程的功能与业务逻辑混合使用
  • 对象生命周期没有问题

当你子类化 QThread

  • 写测试更难
  • 对象清理过程会变得非常混乱,导致奇怪的错误。

Qt 博客中有对该问题的完整描述:您做错了……

QtConcurrent::run 也很方便。

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