一、tha.jion 和 tha.deatch
1. 先讲两个函数的区别
tha.join();
- 主线程等着子线程跑完,再继续往下走
- 主线程会卡在这里,等子线程结束
- 安全、不会出错
tha.detach();
- 将子线程从主线程分离,主线程不再管理子线程,各自独立运行
- 主线程继续往下跑,可能先跑完、直接退出程序
- 子线程变成后台线程(守护线程),继续自己跑
- 极容易出 bug!
2. 放到代码里,会发生什么灾难?
Int a(10);
std::thread tha(funa, a);
tha.detach(); // 这里改成detach
// 主线程直接往下跑!不等子线程!
cout << "main---------------" << endl;
a.Print();
return 0;
结果 1:主线程可能先跑完,直接销毁 a
主线程退出 → 局部变量 a 被析构但子线程还在跑,还在使用已经被销毁的对象 → 悬空对象、非法访问、程序直接崩溃!
结果 2:输出乱序
你会看到输出乱跳:
main---------------
10
Creat Int:...
Copy Int:...
主线程先打印完了,子线程才开始跑。
结果 3:子线程还没跑完,程序已经结束
子线程直接被操作系统强行杀死。
二、条件变量condition_variable
它就是 C++11 专门给线程用的 "等待 + 唤醒" 工具
1. 它到底是干嘛的?
一句话:让一个线程停下来等,直到另一个线程喊它,它才继续跑。
你可以把它理解成:
- 线程 A:我等着,你弄好了叫我
- 线程 B:我弄好了,喊你继续
这就是 condition_variable。
2. 它必须配合谁一起用?
必须配合 std::mutex 互斥锁一起用不能单独用!
因为:
- 等待的时候要解锁
- 唤醒的时候要加锁条件变量自己不会锁,必须靠
mutex。
3. 最核心的 3 个功能
-
**wait()**线程停下来,阻塞等待→ 自动解锁 → 等着被唤醒
-
notify_one() 唤醒一个正在等待的线程
-
notify_all() 唤醒所有正在等待的线程
三、惊群现象
惊群现象 是指:使用 notify_all() 时,一次性唤醒所有等待线程,但只有一个线程能真正工作,其余线程白唤醒、白抢锁,造成 CPU 资源浪费。
在多线程轮流打印场景中:
notify_one可能唤醒错误线程 → 死锁notify_all会产生惊群 → 但安全、不会死锁 所以必须用notify_all。
四、notify_one和notify_all
1.对比
notify_one()
-
只唤醒1个正在 wait 的线程
-
优点:无惊群,效率高
-
缺点:可能唤醒 "错误线程" → 死锁
notify_all()
-
唤醒所有正在 wait 的线程
-
优点:绝对安全,不会死锁
-
缺点:惊群效应,大量线程抢锁,浪费 CPU
2.什么时候用哪个?
用 notify_one 的场景:生产者消费者(一对多)
用 notify_all 的场景:多线程轮流打印 / 条件与线程绑定 / 程序退出