# 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，选读·需联网）
