Files
scuc-qt-course/docs/teaching/examples/qthread_worker/README.md
T
张宗平 83620c0cf5 教案对齐官方方案书:重写为 5 天 C++/Qt 教学设计,补齐 6 个 Qt 技术缺口示例
- docs/teaching/OUTLINE.md、slides/day1-5.html、index.html:替换此前自定义的
  4 天方案,严格对齐 docs/TRAINING_PLAN_2026.md「C++方向」Day1-5 课堂内容
  (Day6-10 为纯项目实作,不在教案范围)
- docs/teaching/examples/:官方方案书要求但仓库 p03/(wiki 抓取内容) 未覆盖的
  QSS/Model-View/JSON/QThread/QPropertyAnimation/QStateMachine 六个技术点,
  各补一个最小可运行示例 + README 教学文档,均已离屏验证跑通;手工维护,
  不属于 gen_part3.py 生成产物
- 根 CMakeLists.txt:接入 docs/teaching/examples 构建(BUILD_QT_PART 分支内),
  不改动生成器管理的 p01/p03 CMakeLists
- 任务卡改为目标/验收标准/基础知识参考三段式模板,custom.css 配套新增样式
- docs/teaching/PRETEST.md:10 题 10 分钟 C++ 摸底测验,验证教案假定的受众
  基础是否成立,并与 OUTLINE.md §0 的分层预案挂钩
2026-07-02 11:31:44 +08:00

57 lines
2.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# QThread 多线程 —— 教学补充示例
> 对应方案书「C++方向 Day4」:"QT 多线程编程(QThread、互斥锁与防止 UI 阻塞机制)"。
> 仓库 `p03/` 的 wiki 抓取内容里没有多线程相关代码,本示例是手工补写的教学
> 补充,**不属于** `tools/gen_part3.py` 的生成产物。
## 概念
Qt 官方推荐的多线程模式是 **worker-object + moveToThread**,而不是继承
`QThread` 重写 `run()`(后者是早期教程常见但容易踩坑的写法:很容易在错误的
线程里调用槽函数)。核心步骤:
1. 把耗时逻辑写成一个普通 `QObject` 子类(`Worker`),逻辑放在一个槽函数里;
2. 创建一个 `QThread` 对象,调用 `worker.moveToThread(&thread)`——这一步之后,
`worker` 的槽函数会在 `thread` 里执行,而不是在创建它的线程里;
3. 用信号槽驱动流程:`thread.started` 触发 `worker.process()`
`worker.resultReady` 触发主线程里的回调;
4. 关键点:**跨线程的信号槽连接默认是"排队连接"queued connection**——
Qt 会自动把回调调度回接收者所在的线程的事件循环里执行,不需要手动加锁
就能安全地把子线程算出来的结果"带回"主线程。
方案书提到的"互斥锁"用于多个线程**同时读写同一块内存**的场景;本示例里子
线程和主线程之间只通过信号槽传递数据(没有共享变量),所以不需要
`QMutex`——这本身也是一个教学点:**优先用信号槽传递数据,退而求其次才用锁**。
## 代码走读
见 [`qt_thread_worker.cpp`](qt_thread_worker.cpp)
1. `Worker::process()` 模拟一次耗时计算(循环求和),完成后 `emit resultReady(sum)`
2. `worker.moveToThread(&workerThread)` 把 worker 挪到子线程;
3. `workerThread.started``worker.process`:线程一启动就开始干活;
4. `worker.resultReady` → 主线程 lambda:打印结果,并判断当前
`QThread::currentThread()` 确实是主线程(证明排队连接真的把回调带回来了);
5. 结果打印后调用 `workerThread.quit()` 结束子线程事件循环,
`workerThread.finished` 再触发 `QCoreApplication::quit()` 结束整个程序。
## 验证方式
```bash
tools/run_qt.sh qt_thread_worker
```
预期终端输出一行"子线程计算结果:...(当前在主线程打印)"。
## 练习方向(供任务卡引用)
-`Worker::process()` 里的耗时计算换成读一个大文件/睡眠几秒,感受主线程
没有被卡住(如果这是一个真实 GUI 程序,界面依然能响应鼠标点击);
- 故意去掉 `moveToThread` 这一行,观察 `resultReady` 的回调是在哪个线程执行的
`QThread::currentThread()` 判断结果会反过来),理解 `moveToThread` 到底做了
什么。
## 参考
- Qt 官方文档:*QThread*、*Threads and QObjects*doc.qt.io,选读·需联网)