如何在QT中停止qThread

10
我想知道如何正确停止QThread。我有一个线程中的无限循环,并且我想在执行特定操作时停止它:
我已经尝试过:
if (thread->isRunning()){
    worker->stop();
    thread->terminate();
}

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

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

谢谢。


关于 terminate 函数:警告:此函数是危险的,不建议使用。线程可以在其代码路径的任何点终止。在线程修改数据时可能会被终止。线程没有机会在自身清理后解锁任何持有的互斥锁等。简而言之,仅在绝对必要时才使用此函数。来源:http://doc.qt.io/qt-4.8/qthread.html#terminate 。您还有哪些文档内容不理解? - Richard Critten
是的,我知道这很危险,所以我想知道如何正确地停止线程。 - iAmoric
在文档中,似乎两种方法(包括使用exit())都告诉线程循环退出。我真的看不出有什么区别。 - iAmoric
请使用 std::atomic<bool>std::atomic_bool 变量,在您的“特定操作”中设置它,并在您的线程过程中测试它并安全退出。 - Richard Critten
参见此答案,其中详细说明了如何将您拥有的无限循环转换为与事件系统和排队槽相互操作的循环,因此更加灵活和易于使用。 - Kuba hasn't forgotten Monica
1个回答

22
一个合适的答案取决于你如何实际使用`QThread`以及你如何实现`stop()`。
Qt中的预期用例假设遵循以下模型:
- 创建一个对象,在响应信号时执行一些有用的工作 - 创建一个`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(),因为事件循环是无限的,它不“知道”您是否已经处理完图像的所有行。此外,请考虑在计算密集型任务而非QThread中使用QtConcurrent

现在,QThread::terminate() 方法以非常不同的方式停止线程。它只是请求操作系统杀死你的线程。操作系统将在代码的任意位置突然终止你的线程。线程堆栈内存将被释放,但该堆栈指向的任何内存都不会被释放。如果线程拥有某些资源(如文件或互斥量),它永远不会释放它们。涉及写入数据到内存的操作可能会在中途停止,并留下内存块(例如对象)未完全填充且处于无效状态。从这个描述中你可以想象出来,除非情况非常特殊并且保持线程运行比获取内存和资源泄漏更糟糕,否则永远不要调用::terminate()

QThread::wait() 是一个便利函数,它等待 QThread 停止执行。它既可与exit() 一起使用,也可与 terminate() 一起使用。

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


在大多数情况下,这是错误的用法。 - ScumCoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接