直接用 std::Thread 启动线程,需手动调用 join() 或 detach(),否则析构时会调用 std::terminate() 导致程序崩溃;参数默认值传递,传引用需用 std::ref();成员函数需传对象指针或引用;异常不自动传播,需手动处理。

直接用 std::thread 就行,不用自己封装线程管理或调用系统 API;但必须手动处理 join() 或 detach(),否则程序会崩溃。
怎么启动一个最简单的线程
把函数(或可调用对象)传给 std::thread 构造函数即可。注意:函数参数是**值传递**,如果要传引用得用 std::ref() 包裹。
-
std::thread t(func);—— 启动后立即运行,不阻塞主线程 -
std::thread t(func, arg1, arg2);—— 参数按值拷贝,arg1在新线程里是副本 -
std::thread t(func, std::ref(x));—— 显式传引用,避免意外拷贝大对象或需要修改原变量 - 如果
func是成员函数,第一个参数必须是对象指针或引用:std::thread t(&MyClass::method, &obj, arg)
线程结束前必须显式处理 join/detach
std::thread 对象析构时,若仍处于“可加入”(joinable)状态,会直接调用 std::terminate() —— 程序立刻退出,无任何提示。
-
t.join();—— 主线程等待该线程结束,适合需同步结果的场景 -
t.detach();—— 把线程设为后台运行,与std::thread对象解绑;之后不能再对它做任何操作 - 常见错误:
std::thread t(func); return;—— 函数返回导致局部t析构,而它还没被join或detach,必崩 - 安全做法:在作用域末尾加
if (t.joinable()) t.join();,或者用 RAII 封装(如scoped_thread类)
传递参数时容易踩的坑
构造 std::thread 时,参数会被完美转发,但默认行为是**拷贝**;若目标函数接收的是左值引用,又没用 std::ref,编译会失败或行为异常。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
void f(int& x) { x = 42; } int a = 0; std::thread t(f, a);——a被拷贝,f修改的是副本,a不变 - 正确写法:
std::thread t(f, std::ref(a));—— 修改生效 - Lambda 捕获也一样:用
[&x]() { x = 42; }捕获引用,否则捕获的是副本 - 移动语义可用:
std::thread t([](std::String s) { /*...*/ }, std::move(s));,避免冗余拷贝
别忘了线程局部存储和异常传播限制
std::thread 本身不传播异常——子线程里抛出的异常不会自动传到主线程,必须自己处理(比如用 std::promise/std::future 手动传递错误信息)。
- 线程局部变量用
thread_local声明,每个线程一份独立副本,生命周期随线程结束而销毁 - 不要在线程函数里裸 throw 异常,除非你确定能 catch 到;更稳妥的是用
try/catch包裹整个线程函数体 - 全局对象的构造/析构可能跨线程竞争,尤其涉及静态局部变量时,c++11 保证其首次访问是线程安全的,但后续访问不保证
真正麻烦的不是怎么开线程,而是资源归属、生命周期管理和数据竞争——std::thread 只负责执行,其余都得你自己兜底。