ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【C++ 面试真题】29. 聊聊 C++ 的线程管理(std::thread)

2026/8/24 15:03:54 拓冰建站 浏览量
【C++ 面试真题】29. 聊聊 C++ 的线程管理(std::thread) 【C 面试真题】聊聊 C 的线程管理std::threadC11 之前想写多线程只能抱 pthread 或 Win32 API跨平台靠宏C11 把线程收进了语言标准。背得出thread 创建线程只是及格真考你的是join 和 detach 不选会怎样、参数怎么传才不会被悬空、jthread 强在哪、和 pthread 比到底赢在哪。本文是多线程篇的开场把thread一次讲透。一、开场 家族有哪些❓ 介绍一下 C11 的线程库✅thread一家三口 一个[C20]新成员成员作用std::thread线程本体创建/join/detachthis_thread当前线程的工具get_id/sleep/yieldjthread[C20]自动 join 的 thread 停止令牌线程工具hardware_concurrency 查核数回答思路先报thread 三件套——创建、汇合、分离再点this_thread的睡眠与让步——面试官多半会追join/detach 不调会怎样和参数传递的坑。二、创建线程三种入口❓ std::thread 怎么创建能跑什么✅ 函数指针、lambda、成员函数全都行// ① 普通函数voidwork(intn);threadt1(work,10);// ② lambda最常用threadt2([]{couthi from lambda;});// ③ 成员函数对象 类::方法structTask{voidrun(){}};Task obj;threadt3(Task::run,obj);构造即启动——没有start这一步。参数直接跟在线程对象后面像转发函数调用一样。三、join 还是 detach没有第三条路❓ 不调 join 也不调 detach 会怎样✅程序直接 terminate 崩溃——这是 std::thread 的死规矩线程对象析构时仍 joinable没 join 没 detach标准库直接std::terminate()。voidf(){threadt(work);if(err)return;// ❌ t 析构 → 崩t.join();}两个选择语义完全不同操作语义风险join等它跑完阻塞当前线程等太久/死等detach放手不管后台自生自灭引用的东西先死 → 悬空// detach 的经典事故voidf(){intlocal42;threadt([]{use(local);});t.detach();// f 返回、local 消亡// 后台线程还在用 → UAF}⚠️detach 三思分离线程的生存期可能超过它引用的一切。要用 detach线程内部只能持有自己独占的资源按值捕获、智能指针 move 进去或用通知退出机制管理生命周期。提前 return 的坑还能用 RAII 收口——给 thread 穿上自动汇合的外衣classJoinGuard{thread t_;public:explicitJoinGuard(thread t):t_(std::move(t)){}~JoinGuard(){if(t_.joinable())t_.join();// 任何路径都汇合}};// f() 里JoinGuard g{thread{work}};if(err)return;// ✅ 析构自动 join这正是[C20]jthread 的思路——标准库替你把这个包装类写好了。四、参数传递默认拷贝是保护也是坑❓ 给线程传参数要注意什么✅参数默认按值拷贝进线程内部存储——这是保护避免悬空引用但想传引用必须显式声明intx0;// ❌ 这里仍是拷贝// thread t(work, x);// 传引用要 refthreadt1(set,ref(x));// ✅ 真引用// 成员函数 参数混搭threadt2(Task::run,obj,arg1,arg2);⚠️ 最阴的坑lambda 按引用捕获局部变量 detach——[]抓的是 f 的栈帧f 一返回全悬空。同上节结论跨线程生命周期要么拷贝、要么智能指针转移所有权。五、this_thread当前线程的三件工具❓ this_thread 都有什么✅ 四个工具全在当前线程身上// ① 我是谁autoidthis_thread::get_id();// ② 睡眠指定时长C14 后缀字面量this_thread::sleep_for(100ms);// ③ 睡到指定时间点this_thread::sleep_until(steady_clock::now()1s);// ④ 让出时间片自旋等待时用this_thread::yield();永远别用 C 的 sleep(3)它阻塞的是进程级信号处理的旧模型且不可移植。this_thread::sleep_for类型安全、跨平台、可配合各种时钟。yield()的正确用法是自旋等待里降低 CPU 占用不是歇一会儿的万能调味料。六、对比 pthread / Win32标准库赢在哪❓ 有了 pthread为什么还要 std::thread✅ 四个维度的差距维度pthread / Win32std::thread跨平台宏地狱两套代码一套代码全平台参数void* 强转来回类型安全任意参数RAII自己封装jthread 自动 join生态孤立mutex/atomic/future 同标准// pthread 时代传参靠 void* 硬转void*work(void*arg){intn*(int*)arg;// 谁懂这类型}// std::thread参数直接进threadt(work,42); 一句话总结pthread 提供的是系统调用包装std::thread 提供的是语言级抽象——类型安全、异常安全配合 RAII、和标准库并发组件无缝拼接。[C20]的 jthread 更进一步析构自动 join、内置stop_token协作式取消把忘 join 崩溃和线程杀不死两大痛点一起解决。七、批量线程vector 管理一小队❓ 开 10 个线程干完活再继续怎么组织✅vectorthread 循环 join多线程版本的fork-joinvectorthreadpool;for(inti0;i10;i)pool.emplace_back(work,i);// 全部汇合for(autot:pool)t.join();要带返回值别用共享变量硬凑——future的async/packaged_task是正路本篇不展开追问见下。⚠️ 注意emplace_back可能抛bad_alloc/system_error线程创建失败严格健壮的代码要接住并 join 已创建的。工程上直接用线程池库如boost::asio::thread_pool更稳。八、面试高频追问❓ Q1thread 对象可以拷贝吗✅ 不可以——thread只能移动move-only和 unique_ptr 一个待遇。放进容器要std::move或emplace_back原地构造。这也保证了一个线程实体只有一个管理者避免 double join。❓ Q2join 之后再 join 会怎样✅std::terminate崩溃。join/detach 之后线程对象不再 joinable再调是错误。多次 join 的防护习惯join 前判if (t.joinable())。❓ Q3jthread 的 stop_token 怎么用✅ 线程函数收stop_token参数循环里查stoken.stop_requested()决定退出外部调jt.request_stop()发出停止请求。析构时 jthread自动 request_stop join——优雅退出的完整闭环比 detach 安全、比杀线程文明。❓ Q4hardware_concurrency 返回什么能当线程数用吗✅ 返回硬件线程上下文数核数 × 超线程是提示不是承诺——可能返回 0探测失败。线程池大小参考它但 IO 密集任务该开更多等待时 CPU 空闲纯计算则超过它只是浪费上下文切换。❓ Q5线程函数抛了异常会怎样✅ 异常出不了线程函数边界——未捕获则std::terminate整个进程崩。要跨线程传异常/结果用std::promise/future线程内set_exception另一端future.get()重新抛出。❓ Q6怎么给线程命名方便调试✅ 标准库没有官方接口——name 是平台扩展pthread_setname_np / SetThreadDescription。工程实践调试期用平台 API 打标或干脆用日志前缀携带 thread::id。❓ Q7线程和进程有什么区别✅ 进程是资源分配的单位独立地址空间、文件表线程是调度执行的单位共享所在进程的地址空间和资源独有栈与寄存器上下文。推论线程间通信天然就是读写共享变量所以才有本篇之后的一切同步话题进程间通信要走管道/共享内存等内核机制一个崩了连累整个进程另一个崩了进程各自安好。九、总结速查表考点一句话结论创建即启动构造 thread 就跑无 startjoin/detach必二选一否则析构 terminatedetach 风险引用对象先死 → UAF传参默认拷贝真引用要 refsleepsleep_for 100ms 字面量yield自旋等待降低占用thread 可拷贝否只能移动jthread [C20]自动 join stop_token批量vector 循环 join跨线程异常promise/future 的 set_exception一句话回顾thread 构造即启动、join/detach 必二选一不选就 terminate参数默认拷贝保护你真引用要refdetach 放走的线程只能持有独立资源标准库赢 pthread 的是类型安全 RAII 生态[C20]jthread 自动 join stop_token 把最后两个痛点补齐。如果您觉得本篇内容对你有帮助欢迎点赞 、收藏 ⭐、转发 。下期我们继续多线程篇——聊互斥锁与读写锁lock_guard/unique_lock 怎么选、死锁怎么破、读写锁什么时候反而更慢敬请关注