我将重点介绍这两种方法之间的差异。没有适合所有用例的通用答案,因此最好准确了解它们的含义,然后选择最适合您的情况。
使用 moveToThread()
moveToThread() 用于控制对象的线程亲和性,这基本上意味着设置对象将发出信号的线程(或更好的Qt事件循环)并且它的槽将被执行。
如您链接的文档中所示,这可用于在不同的线程上运行代码,基本上是创建一个 dummy worker,编写代码以在 public slot(在示例中为 doWork() 插槽),然后使用 moveToThread 将其移动到不同的事件循环。
然后,触发连接到该插槽的信号。由于发出信号的对象(示例中的 Controller)位于不同的线程中,并且信号通过排队连接连接到我们的 doWork 方法,因此 doWork 方法将在 worker 中执行线程。
这里的关键是您正在创建一个由工作线程运行的新事件循环。因此,一旦 doWork 槽启动,整个事件循环将一直忙到它退出,这意味着传入的信号将排队。
继承 QThread()
Qt 文档中描述的另一种方法是继承 QThread。在这种情况下,我们会覆盖 QThread::run() 方法的默认实现,该方法会创建一个事件循环,以运行其他东西。
这种方法本身没有任何问题,尽管有几个问题。
首先,编写不安全的代码非常容易,因为run() 方法是该类中唯一实际运行在另一个线程上的方法。
如果作为一个例子,你有一个在构造函数中初始化然后在 run() 方法中使用的成员变量,你的成员在调用者的线程中被初始化,然后在新线程。
任何可以从调用者或内部 run() 调用的公共方法也是如此。
slots 也会从调用者的线程中执行,(除非你像 moveToThread(this) 那样做一些非常奇怪的事情)导致额外的混乱。
所以,这是可能的,但你真的是靠自己,你必须格外注意。
其他方法
当然,这两种方法都有替代方案,具体取决于您的需要。如果您只需要在 GUI 线程运行时在后台运行一些代码,您可以考虑使用QtConcurrent::run()。
但是,请记住 QtConcurrent 将使用全局 QThreadPool。如果整个池都忙(意味着池中没有可用的线程),您的代码将不会立即运行。
如果您至少使用 C++11,另一种选择是使用较低级别的 API,例如 std::thread。