1 概述
在软件开发中,尤其是在图形用户界面(GUI)应用程序中,多线程编程是一种常见且重要的技术手段。Qt 作为一个成熟的跨平台 C++ 框架,提供了强大而友好的多线程支持,使得开发者能够轻松构建高效、响应迅速且功能强大的应用程序。
Qt 多线程开发通过 QThread 类实现,将耗时任务移至后台线程以保持界面响应。 QThread 的核心 API、自定义线程类创建方法、线程间通信机制(信号槽),并通过倒计时代码示例演示了具体实现。重点强调了 UI 操作必须在主线程执行、连接类型选择及资源管理最佳实践,帮助开发者构建高效稳定的跨平台应用。

在软件开发中,尤其是在图形用户界面(GUI)应用程序中,多线程编程是一种常见且重要的技术手段。Qt 作为一个成熟的跨平台 C++ 框架,提供了强大而友好的多线程支持,使得开发者能够轻松构建高效、响应迅速且功能强大的应用程序。
传统单线程程序在执行耗时操作时,往往会阻塞主线程,导致用户界面无响应,给用户带来不良体验。多线程允许我们将耗时操作(如网络请求、文件读写、复杂计算、数据解析等)移至后台线程执行,而主线程则专注于 UI 更新和用户交互,从而保持界面的流畅性和响应性。
Qt 对多线程的支持不仅封装了底层系统 API(如 pthread 或 Windows 线程 API),还引入了信号槽机制与线程安全的对象通信方式,极大地简化了多线程编程的复杂度。此外,Qt 的多线程模型与 Qt 的事件循环(Event Loop)和对象树(Object Tree)深度集成,为开发者提供了统一的编程体验。
QThread 是 Qt 中用于表示和控制线程的核心类。每个 QThread 对象代表一个独立的执行线程。它提供了线程生命周期管理、优先级设置、状态查询等功能,是构建多线程应用的基石。
这是线程的入口函数,必须被子类重写。线程启动后,run() 方法中的代码将在新线程中执行。开发者应在此处实现线程的核心逻辑。
启动线程的方法。调用 start() 后,线程会进入就绪状态,并最终执行 run() 方法。操作系统会根据设定的优先级调度线程执行。如果线程已经在运行,再次调用 start() 不会有任何效果。
静态方法,返回一个指向当前执行线程的 QThread 指针。这在需要识别线程上下文时非常有用。
返回线程当前是否正在运行。可用于检查线程状态,以便决定是否启动、停止或等待线程。
这些方法用于使当前线程休眠指定时长,单位分别为秒、毫秒和微秒。常用于模拟耗时操作或控制线程执行节奏。
阻塞当前线程,直到目标线程执行完毕或等待超时。该方法与 POSIX 的 pthread_join() 功能类似,是线程同步的常用手段。
强制终止线程的执行。由于此方法可能导致资源未释放或状态不一致,应谨慎使用,通常建议通过信号或标志位让线程优雅退出。
在线程执行结束(即 run() 方法返回)时发出。连接此信号可在线程结束时进行资源清理、状态更新等操作。
使用 Qt 进行多线程开发,通常需要创建一个继承自 QThread 的自定义类。在这个类中重写 run() 方法,将需要在线程中执行的逻辑放在其中。这种方式清晰地将线程逻辑与主线程分离,便于维护和理解。
start() 方法启动线程。注意,不应直接调用 run() 方法,否则代码仍将在调用者线程中执行,失去多线程意义。run() 方法中的代码将在新线程中运行。run() 方法返回,线程自动结束,并发出 finished() 信号。由于 Qt 的对象默认不是线程安全的,直接在不同线程间访问对象可能引发问题。Qt 推荐使用信号槽机制进行线程间通信:
QMetaObject::invokeMethod:该方法可在指定线程中调用对象的槽函数或可调用的方法,是另一种安全的跨线程调用方式。connect() 函数的第五个参数 Qt::ConnectionType 在多线程环境中尤为重要。常用的有:
Qt::AutoConnection(默认):自动判断,跨线程时为队列连接。Qt::QueuedConnection:确保槽在接收者线程的事件循环中被调用,是跨线程通信的安全方式。finished() 信号与 deleteLater() 方法安全清理对象。run() 方法正常返回),避免使用 terminate() 强制终止。thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QWidget>
#include <QThread>
class Thread : public QThread {
Q_OBJECT
public:
Thread();
// 重要的目的是重写父类的 run 方法.
void run();
signals:
void notify();
};
#endif // THREAD_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "thread.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
Thread thread;
void handle();
};
#endif // WIDGET_H
thread.cpp
#include "thread.h"
Thread::Thread() {}
void Thread::run() {
for (int i = 0; i < 10; i++) {
// sleep 本身是 QThread 的成员函数,就可以直接调用
sleep(1);
// 发送一个信号,通知主线程
emit notify();
}
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
connect(&thread, &Thread::notify, this, &Widget::handle);
thread.start();
}
Widget::~Widget() {
delete ui;
}
void Widget::handle() {
int value = ui->lcdNumber->intValue();
value--;
ui->lcdNumber->display(value);
}
Qt 的多线程框架为开发者提供了一套高效、安全且易于使用的工具集。通过 QThread 类,我们可以轻松创建和管理线程,将耗时任务移至后台,从而提升应用程序的整体性能和用户体验。信号槽机制与事件循环的结合,使得跨线程通信既安全又简便,极大地降低了多线程编程的复杂性。
在实际开发中,合理规划线程分工、严格遵守 UI 线程限制、妥善处理线程间同步与通信,是构建健壮多线程应用的关键。随着计算机硬件多核心的普及,掌握 Qt 多线程编程已成为现代 GUI 开发者不可或缺的技能之一。
多线程编程虽然强大,但也会引入诸如竞争条件、死锁、资源管理等挑战。因此,在深入使用前,应该充分理解线程原理,并结合 Qt 文档和最佳实践,逐步构建稳定高效的多线程应用程序。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online