一个合适的答案取决于你如何实际使用`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 信号和插槽将无法正常工作。
terminate
函数:警告:此函数是危险的,不建议使用。线程可以在其代码路径的任何点终止。在线程修改数据时可能会被终止。线程没有机会在自身清理后解锁任何持有的互斥锁等。简而言之,仅在绝对必要时才使用此函数。来源:http://doc.qt.io/qt-4.8/qthread.html#terminate 。您还有哪些文档内容不理解? - Richard Crittenstd::atomic<bool>
或std::atomic_bool
变量,在您的“特定操作”中设置它,并在您的线程过程中测试它并安全退出。 - Richard Critten