什么时候应该moveToThread
优先于子类QThread
?
This link表明两种方法都有效。我应该在什么基础上决定从这两者中使用什么?
答案 0 :(得分:16)
我会关注两种方法之间的差异。没有适用于所有用例的一般答案,因此最好准确理解他们选择最适合您案例的内容。
moveToThread()用于控制对象的线程关联性,这基本上意味着设置对象将从中发出信号的线程(或更好的Qt事件循环)它的插槽将被执行。
如您链接的文档中所示,这可用于在不同的线程上运行代码,基本上创建虚拟工作者,编写代码以在公共插槽中运行(在示例中 doWork()插槽),然后使用 moveToThread 将其移动到其他事件循环。
然后,触发连接到该槽的信号。由于发出信号的对象(示例中的 Controller )位于不同的线程中,并且信号通过排队连接连接到我们的doWork方法,因此doWork方法将在worker中执行线程。
这里的关键是你正在创建一个由工作线程运行的新事件循环。因此,一旦doWork插槽启动,整个事件循环将一直忙,直到它退出,这意味着传入的信号将排队。
Qt文档中描述的另一种方法是继承QThread。在这种情况下,一个覆盖QThread :: run()方法的默认实现,该方法创建一个事件循环,以运行其他东西。
这种方法本身没有任何问题,尽管有几次捕获。
首先,编写不安全的代码非常容易,因为 run()方法是该类中唯一一个实际在另一个线程上运行的方法。
例如,如果您有一个在构造函数中初始化的成员变量,然后在 run()方法中使用,那么您的成员将在调用者的线程中初始化,然后用于新线程。
可以从调用者或内部run()调用的任何公共方法的相同故事。
此外,还会从调用方的线程执行插槽(除非你做了一些非常奇怪的事情,因为 moveToThread(this))会导致额外的混乱。
所以,这是有可能的,但是你真的对这种方法有所了解,你必须特别注意。
这两种方法当然都有替代方案,具体取决于您的需求。如果您只需要在GUI线程运行时在后台运行一些代码,您可以考虑使用QtConcurrent::run()。
但是,请记住,QtConcurrent将使用全局 QThreadPool 。如果整个池都很忙(意味着池中没有可用的线程),则代码将不会立即运行。
另一种选择,如果您至少在C ++ 11上,则使用较低级别的API,例如std::thread。
答案 1 :(得分:8)
作为起点:不要使用。在大多数情况下,您有一个工作单元,您希望以异步方式运行。请使用QtConcurrent::run
。
如果你有一个对事件作出反应和/或使用计时器的对象,那么它应该是非阻塞的QObject
并进入一个线程,可能与其他对象共享。
这样的对象也可以包装阻塞API。
在实践中,从不需要子类化QThread
。它就像是对QFile
进行子类化。 QThread
是一个线程句柄。它包装了一个系统资源。重载它有点傻。
答案 2 :(得分:3)
QThread是低级线程抽象,首先看一下高级API QtConcurrent模块和QRunnable
如果这些内容中没有任何内容适合您,请阅读this old article,它会说明您应该如何使用QThread。将此线程中执行的线程和任务视为单独的对象,不要将它们混合在一起。
因此,如果您需要编写自定义,特定或扩展的线程包装器,那么您应该将QThread子类化。
如果你有带信号和槽的QObject派生类,那么就在它上面使用moveToThread。
在其他情况下使用QtConcurrent,QRunnable和QThreadPoll。
答案 3 :(得分:0)
简单的答案总是如此。 将对象移动到线程时:
当您继承QThread
Qt博客中有完整的问题描述:You’re doing it wrong…。
QtConcurrent::run
也非常方便。
请记住,默认情况下,当从其他线程对象发送信号时,插槽会尝试在踏板之间跳转。有关详细信息,请参阅Qt::ConnectionType的文档。