
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- [1 ~> Qt 多线程 QThread 基础总览](#1 ~> Qt 多线程 QThread 基础总览)
-
- [1.1 Qt 多线程与系统线程的关系](#1.1 Qt 多线程与系统线程的关系)
- [1.2 QThread 设计思路:继承重写 run ()](#1.2 QThread 设计思路:继承重写 run ())
-
- [1.2.1 线程入口函数](#1.2.1 线程入口函数)
- [1.3 QThread 核心 API](#1.3 QThread 核心 API)
- [2 ~> QThread 继承方式实战:多线程倒计时案例](#2 ~> QThread 继承方式实战:多线程倒计时案例)
-
- [2.1 核心约束:Qt GUI 线程规则](#2.1 核心约束:Qt GUI 线程规则)
- [2.2 项目实现步骤](#2.2 项目实现步骤)
-
- [2.2.1 thread.h(线程子类头文件)](#2.2.1 thread.h(线程子类头文件))
- [2.2.2 thread.cpp(线程子类实现)](#2.2.2 thread.cpp(线程子类实现))
- [2.2.3 widget.h(主窗口头文件)](#2.2.3 widget.h(主窗口头文件))
- [2.2.4 widget.cpp(主窗口实现)](#2.2.4 widget.cpp(主窗口实现))
- [2.3 程序时序流程](#2.3 程序时序流程)
- [3 ~> Qt 客户端多线程应用场景](#3 ~> Qt 客户端多线程应用场景)
-
- [3.1 服务器多线程 vs Qt 客户端多线程目标差异](#3.1 服务器多线程 vs Qt 客户端多线程目标差异)
- [3.2 客户端阻塞根源](#3.2 客户端阻塞根源)
- [3.3 正确处理方案](#3.3 正确处理方案)
- [3.4 典型业务场景](#3.4 典型业务场景)
- [4 ~> 重要补充知识点](#4 ~> 重要补充知识点)
-
- [4.1 对象依附线程(Thread Affinity)](#4.1 对象依附线程(Thread Affinity))
- [4.2 QThread 两种主流用法](#4.2 QThread 两种主流用法)
- [4.3 线程退出注意事项](#4.3 线程退出注意事项)
- [4.4 信号槽连接类型](#4.4 信号槽连接类型)
- 结尾

1 ~> Qt 多线程 QThread 基础总览
1.1 Qt 多线程与系统线程的关系
- Qt 多线程底层本质仍是操作系统原生线程,Linux 下对应 pthread,Windows 对应系统线程 API,Linux 线程的原理、线程安全约束全部适用于 Qt 多线程。
- Qt 对不同操作系统平台原生线程 API 做跨平台封装,实现一套代码多平台编译运行。
- 并发技术对比
- Linux
pthread:C 语言原生库,API 繁琐,大量使用void*通用指针,内存管理复杂,实际业务开发很少直接使用。 - C++11
std::thread:对系统线程封装,支持 lambda、可调用对象,具备类型检查,规避void*,C++ 开发中使用广泛;底层依然封装操作系统线程。 - Qt
QThread:面向对象封装,提供事件循环、信号槽线程间通信机制,更适配 Qt GUI 客户端开发。
- Linux
**注意:**掌握操作系统原生线程原理,是理解上层
std::thread、QThread的前置基础。
1.2 QThread 设计思路:继承重写 run ()
1.2.1 线程入口函数
- 线程的核心:指定线程入口函数,新线程被操作系统调度后执行入口函数内业务逻辑。
std::thread/pthread:传入函数指针、lambda 表达式作为入口。- QThread 传统用法:继承
QThread子类,重写虚函数 **run()**,利用 C++ 多态实现线程入口。run()不会由开发者手动调用;调用QThread::start()后,操作系统创建内核线程,自动调用子类重写的run()。- 多态会带来虚函数表运行时查找的微小开销;对于游戏引擎、高性能服务器追求极致性能场景会规避;Qt 客户端 GUI 开发对该开销不敏感,属于常规开发模式。
1.3 QThread 核心 API
| API | 功能说明 |
|---|---|
virtual void run() |
线程入口虚函数,子类重写,线程业务逻辑编写位置,禁止手动调用 |
void start(Priority = InheritPriority) |
向操作系统发起创建线程请求;线程已运行时调用无效果;线程就绪后自动执行run() |
static QThread* currentThread() |
静态方法,返回当前正在执行代码所属线程的QThread对象指针 |
bool isRunning() const |
判断内核线程是否处于运行状态,run()未执行完毕返回 true |
sleep(unsigned long secs) |
线程休眠,单位秒;仅作用于调用该函数的线程 |
msleep(unsigned long msecs) |
线程休眠,单位毫秒 |
usleep(unsigned long usecs) |
线程休眠,单位微秒 |
bool wait(unsigned long time = ULONG_MAX) |
阻塞等待线程结束,等价 Linuxpthread_join();等待线程执行完毕或者超时返回;线程未启动直接返回 true;超时返回 false;永久阻塞使用默认参数 |
void terminate() |
强制终止线程,操作系统调度时机不确定,资源无法安全释放,业务开发禁止使用 |
void finished()信号 |
线程执行完毕发射该信号,可绑定槽做线程资源清理工作 |
重点:
sleep/msleep/usleep是线程休眠,会阻塞当前调用线程;GUI 主线程禁止调用休眠,会冻结界面事件循环。
2 ~> QThread 继承方式实战:多线程倒计时案例
2.1 核心约束:Qt GUI 线程规则
- 所有控件、界面组件只能在主线程(GUI 线程)操作修改,其他子线程禁止直接读写 UI 控件,会引发程序崩溃。
- 子线程只负责计算、计时、IO 处理;子线程通过自定义信号发送数据,信号槽跨线程通知主线程,主线程槽函数内部更新界面。
- 跨线程信号槽默认自动使用
Qt::QueuedConnection队列连接,事件循环分发到接收者所属线程执行槽函数。
2.2 项目实现步骤
- 创建 Qt Widget 项目,UI 界面放置
QLCDNumber数字显示控件。 - 创建 C++ 类
Thread,继承QThread,启用Q_OBJECT宏。 - 子类重写
run()作为线程入口;定义自定义信号用于向主线程通知计时事件。 - Widget 主线程创建 Thread 对象;connect 绑定信号槽;调用
start()启动子线程。 - 子线程 run 循环计时休眠,通过 emit 发射信号;主线程槽函数接收信号更新 LCD 显示。
2.2.1 thread.h(线程子类头文件)
cpp
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
explicit Thread();
protected:
//重写线程入口函数run,protected虚函数
void run() override;
signals:
//自定义信号,无需实现,MOC工具自动生成实现
void notify();
};
#endif // THREAD_H
2.2.2 thread.cpp(线程子类实现)
cpp
#include "thread.h"
Thread::Thread()
{
}
void Thread::run()
{
//子线程执行逻辑,禁止操作UI控件
for(int i = 0; i < 10; ++i)
{
//休眠1秒,阻塞当前子线程,不会影响GUI主线程
sleep(1);
//发射自定义信号,通知主线程更新界面
emit notify();
}
}
2.2.3 widget.h(主窗口头文件)
cpp
#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 slots:
//槽函数:接收子线程信号,更新UI
void handle();
private:
Ui::Widget *ui;
Thread thread; //子线程对象
};
#endif // WIDGET_H
2.2.4 widget.cpp(主窗口实现)
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//绑定子线程notify信号到主线程handle槽函数
//跨线程自动使用QueuedConnection
connect(&thread, &Thread::notify, this, &Widget::handle);
//启动子线程,调用操作系统API创建内核线程,自动执行run()
thread.start();
}
Widget::~Widget()
{
//析构前确保线程执行完毕,防止对象销毁线程还在运行
thread.wait();
delete ui;
}
void Widget::handle()
{
//槽函数运行在主线程,可以安全操作UI控件
int value = ui->lcdNumber->intValue();
value--;
ui->lcdNumber->display(value);
}
2.3 程序时序流程
- Widget 构造函数执行,
thread.start()调用,操作系统创建子线程。 - 子线程调度执行
Thread::run(),循环执行sleep(1),每 1 秒emit notify()发射信号。 - 信号跨线程投递,放入主线程事件队列。
- 主线程事件循环取出事件,执行
handle()槽函数,更新 LCD 数字。 - for 循环执行 10 次结束,
run()函数 return;线程结束,发射finished()信号。
3 ~> Qt 客户端多线程应用场景
3.1 服务器多线程 vs Qt 客户端多线程目标差异
- 服务器端多线程:主要目标利用 CPU 多核,并行计算,充分压榨 CPU 算力,提升任务处理速度。
- Qt 客户端 GUI 程序多线程核心目标:解阻塞,保护主线程事件循环,避免界面卡死。
3.2 客户端阻塞根源
GUI 主线程职责:运行事件循环,处理鼠标、键盘、窗口绘制全部用户交互。
- 如果主线程执行耗时 IO 操作(网络大文件上传下载、大量磁盘读写、资源加载),主线程被系统阻塞挂起,事件循环停止。
- 现象:窗口无响应,操作系统弹出程序未响应提示,用户无法操作界面。
3.3 正确处理方案
- 将耗时 IO、等待类业务放到独立子线程执行;阻塞只会发生子线程。
- 主线程持续运行事件循环,持续响应用户操作。
- 子线程计算 / IO 完成,通过信号槽把结果回传给主线程更新界面。
3.4 典型业务场景
- 网络:大文件上传下载、网络请求等待响应。
- IO:大批量磁盘文件读写、加载大型资源文件。
- 后台计算:耗时数据处理,不允许卡住界面。
4 ~> 重要补充知识点
4.1 对象依附线程(Thread Affinity)
- QThread 对象本身属于创建它的线程,不是子线程;只有
run()内部执行代码运行在内核子线程。 - 继承 QThread 方式中,不要给 QThread 子类增加成员对象,在 run 外部做信号槽绑定 ,成员对象会依附主线程,
run()子线程中操作会产生跨线程访问风险。
4.2 QThread 两种主流用法
- 继承重写
run()(本笔记示例用法),适合业务逻辑完整、循环常驻运行的任务。 QObject + moveToThread():不继承 QThread,将工作对象移动到 QThread,适合使用信号槽驱动、存在事件循环的任务(Qt 官方更推荐)。
4.3 线程退出注意事项
- 禁止调用
terminate()强制杀死线程,资源泄漏风险。 - 循环任务线程,推荐设置原子 bool 标记位,控制 run 循环正常退出,外部调用
wait()等待线程安全结束。 - 对象析构前,务必保证线程已经结束,避免对象销毁,线程还在访问成员造成崩溃。
4.4 信号槽连接类型
Qt::AutoConnection默认:发送与接收同一线程直接调用;跨线程自动变为Qt::QueuedConnection,投递到接收线程事件循环执行。Qt::QueuedConnection:信号发送线程立即返回,槽函数在接收者线程事件循环执行,跨线程安全。Qt::DirectConnection:槽函数直接在信号发射线程执行,跨线程使用会引发线程安全问题。
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!"
"技术之路难免有困惑,但同行的人会让前进更有方向。" |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
