C++ 多线程 10 分钟入门:从“卡死界面”到“丝滑并发”
摘要:互斥锁(mutex)是确保多线程安全访问共享资源的同步机制,同一时间仅允许一个线程操作。不加锁会导致数据竞争(如计数器未达预期值)。使用lock_guard(基于RAII)比手动lock/unlock更安全,能自动释放锁并避免死锁。实战示例展示了链表插入时互斥锁的必要性——防止结构破坏。关键点:1)识别共享资源;2)声明全局互斥锁;3)用lock_guard包裹临界区。类比公共厕所的单坑位规
·
读完这篇,你将掌握:
- 线程 vs 进程(厕所 vs 公司)
- 为什么 counter++ 会“丢数”?
- join vs detach 哪个该用?
- 一行 lock_guard 解决数据竞争
- std::async 快速实现“线程池”效果
1. 线程到底是个啥?(1 分钟类比)
| 概念 | 类比 |
|---|---|
| 进程(Process) | 一家公司 |
| 线程(Thread) | 公司里的员工 |
所有员工共享公司资源(内存、文件、网络),但可以 同时干不同活。
2. 现实中哪里需要多线程?(你正在用)
| 场景 | 主线程 | 子线程 |
|---|---|---|
| 视频播放器 | 显示 UI | ① 解码视频 ② 下载弹幕 ③ 监听键盘 |
| 游戏引擎 | 渲染画面 | ① 物理碰撞 ② 播放音效 |
| 服务器 | 接受连接 | 每个客户端一个线程 |
| 数据分析 | 显示进度条 | 并行计算 |
核心好处:
- 提速(CPU 多核并行)
- 不卡顿(UI 线程不被阻塞)
3. C++ 如何创建线程?(3 行代码起步)
cpp
#include <iostream>
#include <thread>
using namespace std;
void work() {
cout << "子线程:正在搬砖...\n";
}
int main() {
thread t(work); // ① 创建线程
cout << "主线程:我先喝口茶...\n";
t.join(); // ② 等待子线程结束
cout << "主线程:干完了!\n";
}
输出(顺序不定):
text
主线程:我先喝口茶...
子线程:正在搬砖...
主线程:干完了!
t.join() = 主线程排队等子线程 不加 join()?主线程直接退出 → 程序崩溃!
4. 多个线程并行运行(顺序随机!)
cpp
void task(int id) {
cout << "线程 " << id << " 上线啦!\n";
}
int main() {
thread t1(task, 1), t2(task, 2), t3(task, 3);
t1.join(); t2.join(); t3.join();
}
输出示例(每次不同):
text
线程 2 上线啦!
线程 1 上线啦!
线程 3 上线啦!
并行 ≠ 顺序,这就是多线程的魅力(也是坑)
5. 致命陷阱:共享变量导致“数据竞争”
cpp
int counter = 0;
void add() {
for (int i = 0; i < 100000; ++i)
counter++; // 危险!不是原子操作
}
cpp
thread t1(add), t2(add);
t1.join(); t2.join();
cout << counter << endl; // 期望 200000,实际常 < 200000 ❌
counter++ 实际是 读 → 加1 → 写,两线程交叉 → 覆盖丢失
6. 一行代码解决:std::lock_guard(推荐!)
cpp
#include <mutex>
mutex mtx; // 全局一把锁
void add_safe() {
for (int i = 0; i < 100000; ++i) {
lock_guard<mutex> lock(mtx); // 自动 lock + unlock
counter++;
} // ← 作用域结束,自动解锁(异常也安全)
}
结果:counter 永远是 200000 ✅
需要详细了解互斥锁的可以->互斥锁
7. 线程常用操作速查表
| 操作 | 含义 | 推荐场景 |
|---|---|---|
| t.join() | 等待线程结束 | 必须等结果时 |
| t.detach() | 后台独立运行 | 日志、监控等 “火了就跑” |
| this_thread::sleep_for() | 线程睡觉 | 模拟耗时 / 限流 |
| this_thread::get_id() | 获取线程 ID | 调试 / 日志 |
cpp
this_thread::sleep_for(chrono::seconds(2)); // 睡 2 秒
cout << "我的 ID 是: " << this_thread::get_id() << endl;
8. join vs detach:
| 方法 | 效果 | 风险 |
|---|---|---|
| join() | 主线程阻塞等待 | 安全,但可能卡住 |
| detach() | 子线程后台运行 | 不能再操作 t,否则 undefined behavior |
cpp
thread t(task);
t.detach(); // 放飞它,主线程不等
// t.join(); // 报错!已 detach
9. 线程池雏形:std::async(超简单)
频繁创建线程 = 开销大 → 用 线程池
cpp
#include <future>
#include <iostream>
using namespace std;
int compute(int x) {
this_thread::sleep_for(chrono::seconds(1));
return x * 2;
}
int main() {
auto f1 = async(launch::async, compute, 10); // 异步执行
auto f2 = async(launch::async, compute, 20);
cout << "结果: " << f1.get() + f2.get() << endl; // 60
}
std::async 自动复用线程,适合 临时并行任务
10. 多线程速查表
| 概念 | 说明 | 必备工具 |
|---|---|---|
| 线程 | 程序执行最小单位 | <thread> |
| 多线程 | 同时跑多个任务 | thread t(func) |
| 数据竞争 | 共享变量未加锁 | counter++ |
| 互斥锁 | 保护共享资源 | mutex + lock_guard |
| join | 等待线程结束 | t.join() |
| detach | 后台运行 | t.detach() |
| async | 异步 + 自动线程池 | std::async |
行动清单(3 步开启多线程)
- 找到并行任务(如下载 + UI)
- 用 thread t(func) 创建线程
- 共享变量?必须加 lock_guard<mutex>
互斥锁
lock_guard<mutex> lock(mtx); // 一行搞定安全
生活类比:公司 vs 员工
公司(进程):有办公室、电脑、打印机 员工(线程):
- 共享打印机(内存)
- 同时开会、写代码、接电话
- 但打印时要排队 → 加锁!
更多推荐



所有评论(0)