如何在QT中停止qThread

时间:2017-03-08 19:04:04

标签: c++ qt qthread

我想知道如何正确停止QThread。我在一个线程中有一个无限循环,我想在我做一个特定的动作时停止它:

我试过了:

if (thread->isRunning()){
    worker->stop();
    thread->terminate();
}

stop()方法将值设置为false以退出无限循环。

此外,我并不完全理解quit(),terminate()或wait()之间的区别。有人可以解释一下吗?

感谢。

1 个答案:

答案 0 :(得分:7)

正确答案取决于您实际使用QThread的方式以及您如何实现stop()。

Qt中的预期用例假定以下模型:

  • 您创建的对象将执行一些有用的工作以响应Signals
  • 您创建了一个QThread并将您的对象移动到此线程
  • 当您向对象发送信号时,它已在您创建的QThread中处理

现在你需要了解一些实际实现方式的内部结构。 Qt中有几种信号“模型”,在某些情况下,当您“发送信号”时,您实际上只是简单地调用“插槽”功能。这是一个“直接”插槽连接,在这种情况下,slot()将在调用者线程中执行,这是一个引发信号的线程。因此,为了与另一个线程通信,Qt允许另一种信号“排队连接”。调用者不是调用slot(),而是将消息留给拥有此插槽的对象。与此对象关联的线程将读取此消息(稍后某个时间)&执行slot()本身。

现在您可以了解创建和执行QThread时发生的情况。新创建的线程将执行QThread :: run(),默认情况下,它将执行QThread :: exec(),但这是一个无限循环,它查找与线程关联的对象的消息并将它们传输到这些对象的插槽中。调用QThread :: quit()将终止消息发布到此队列。当QThread :: exec()读取它时,它将停止进一步处理事件,退出无限循环并轻轻终止线程。

现在,正如您可能猜到的那样,为了接收终止消息,必须满足两个条件:

  • 您应该运行QThread :: exec()
  • 您应该退出当前正在运行的插槽

当人们从QThread继承并使用他们自己的代码覆盖QThread :: run时,通常会违反第一个。在大多数情况下,这是错误的用法,但它仍然被广泛传授和使用。在你的情况下,似乎你违反了第二个要求:你的代码运行无限循环,因此QThread :: exec()根本没有得到控件,也没有机会检查它是否需要退出。将你的无限循环丢弃到回收站,QThread :: exec()已经为你运行了这样的循环。考虑如何重新编写代码,使运行无限循环,这总是可行的。根据“消息到线程”概念来考虑您的程序。如果您定期检查某些内容,请创建一个QTimer,它将向您的对象发送消息并在您的插槽中执行检查。如果您处理大量数据,请将此数据拆分为较小的块并编写您的对象,以便它一次处理一个块以响应某些消息。例如。如果您逐行处理图像,请创建一个槽processLine(int line)并将一系列信号“0,1,2 ... height-1”发送到该槽。请注意,一旦完成处理,您还必须显式调用QThread :: quit(),因为事件循环是无限的,当您处理图像的所有行时它不会“知道”。还要考虑将QtConcurrent用于QThread的计算密集型任务。

现在,QThread :: terminate()确实以一种非常不同的方式停止了一个线程。它只是要求OS杀死你的线程。操作系统会简单地将您的线程突然停在代码中的任意位置。线程堆栈内存将是空闲的,但此堆栈指向的任何内存都不会。如果一个线程拥有某些资源(例如文件或互斥),它将不会释放它。涉及将数据写入存储器的操作可以在中间停止并且使存储器块(例如,对象)不完全填充并且处于无效状态。正如你可能从这个描述中猜到的那样,你永远不应该调用:: terminate(),除非极少数情况下保持线程运行比获取内存更糟糕。资源泄漏。

QThread :: wait()只是一个便利功能,等待QThread停止执行。它将与exit()和terminate()一起使用。

您还可以从QThread实现自己的子类的线程系统,并实现自己的线程终止过程。退出线程所需要的只是在必要时从QThread :: run()返回,并且你不能同时使用exit()和terminate()。创建自己的同步原语并使用它来表示要返回的代码。但在大多数情况下,这不是一个好主意,请记住(除非你自己使用QEventLoop),Qt信号和插槽在这种情况下将无法正常工作。