std::thread:多线程入门
创建线程、join 与 detach
商用
◎学完你会
- 用 std::thread 创建并启动线程;
- 理解 join(等待结束)与 detach(分离后台)的差别;
- 知道线程生命周期管理的坑(悬空、忘 join)。
1开一个线程干活,主线程等着它
std::thread t(f); 启动线程跑 f;t.join() 等它结束,t.detach() 让它后台自生自灭。
std::thread t(worker);
t.join(); // 等 t 结束
看两种生命周期管理。
线程对象必须 join 或 detach 之一,否则析构会 std::terminate。
2关键命令
| 写法 | 含义 |
std::thread t(fn, args...); | 创建线程,用 fn(args...) 作为线程函数 |
t.join() | 阻塞等待 t 线程结束(回收资源) |
t.detach() | 分离:线程转入后台,t 不再关联它 |
std::this_thread::sleep_for(…) | 当前线程睡眠指定时长 |
✅ join 一定别忘:线程对象析构时若既没 join 也没 detach,会调用 std::terminate 终止程序。
3线程生命周期:join vs detach
join:主线程停下来等它干完再继续——适合"必须等结果"。detach:线程在后台继续,主线程不等它——适合"后台任务/火忘掉";但 detach 后线程引用已销毁的局部变量会悬空。
void worker(){ for(int i=0;i<1000;i++) cout << i; }
std::thread t(worker);
t.join(); // 主线程等 worker 全部跑完
// 危险示例:detach 后 worker 用到即将销毁的局部变量
void bad(){ int x=5; std::thread t([&]{ while(true) cout<<x; }); t.detach(); } // x 提前销毁 → 悬空
要点:线程要访问的数据必须活得比线程久。detach 的线程生命周期不可控,访问栈上/局部数据极易悬空;能用 join 尽量 join,或用 async 管理生命周期(见 33.3)。
!易错点
① 忘 join 又忘 detach——线程对象析构触发 std::terminate。
② detach 后线程访问已销毁局部变量——悬空、未定义行为。
③ 给线程传引用没包 ref——std::thread t(f, x) 默认按值拷贝,改不了原变量;要 std::ref(x)。
④ 多线程同时改共享数据无锁——竞态(见 33.2/33.4)。
?跨学科:多线程,像"餐厅同时开几个窗口"
std::thread 像再开一个服务窗口(线程),多个窗口同时接待(并行干活),总吞吐更高。join 像"顾客在窗口前等这一单做完再走";detach 像"点完餐就离开,后厨做好自己放保温台",但没人盯着它,一旦它去拿已下架食材(销毁的变量)就出乱子。
核心教训:开多少窗口都要能收尾(join),别让没人管的窗口乱跑还引用着已撤掉的东西。
✎练一练
创建一个执行函数 worker 的线程,正确写法是?
std::thread t(worker); 用线程函数构造并启动线程。
t.join() 与 t.detach() 的区别是?
join 等待线程结束;detach 分离到后台、主线程不等(要当心悬空)。
线程对象既没 join 也没 detach 就析构,会发生什么?
线程对象析构时若还处于可关联状态,会 std::terminate。必须 join 或 detach 之一。