std::thread是可移动的线程句柄,不管理线程生命周期,仅负责启动和可选等待;析构前必须调用join()或detach(),否则触发std::terminate();参数全被拷贝,传引用需std::ref,lambda避免[&]捕获;不支持引用返回,异常不跨线程传播,须自行捕获。

std::thread 就是个可移动的线程句柄,不是线程本身
它不管理线程生命周期,只负责启动和(可选)等待。你构造一个 std::thread 对象,就等于把函数扔进操作系统线程池里跑——但一旦构造完,std::thread 对象只是个“凭证”,线程自己在后台执行。
常见错误现象:std::terminate() 直接炸掉,原因几乎都是 std::thread 对象析构前没调用 join() 或 detach()。
- 必须在对象销毁前决定:是等它结束(
join()),还是彻底放手(detach()) -
join()会阻塞当前线程,直到目标线程退出;detach()后线程独立运行,资源由系统回收 - 调用过
join()或detach()后,std::thread对象进入“未关联”状态,再次调用会抛std::system_error
怎么传参给线程函数?别直接传引用或临时对象
构造 std::thread 时,所有参数会被**拷贝**进新线程的私有栈空间。这意味着:引用参数会变成悬空引用,std::move 的临时对象可能在子线程开始执行前就被销毁。
使用场景:需要在线程里访问外部变量,比如 vector、string、自定义结构体。
立即学习“C++免费学习笔记(深入)”;
- 想传引用?用
std::ref(x)包一层,确保传递的是引用包装器 - 想传右值但又怕被提前析构?用
std::move(x)没问题,但前提是x是局部变量且生命周期覆盖线程启动过程 - lambda 捕获也一样:捕获
[&]是危险的,改用[=]或显式捕获需要的变量(如[x, &y])
示例:std::thread t([](int a, std::string s) { /* ... */ }, 42, std::string("hello")); —— 这里的 std::string("hello") 会被拷贝进新线程,安全。
线程函数签名限制:只能接受可调用对象,且不能有引用返回
std::thread 构造函数模板要求第一个参数是“可调用的”,比如函数指针、lambda、绑定对象(std::bind)、重载了 operator() 的类实例。但它对返回值完全不关心——因为线程启动后无法获取返回值。
常见错误现象:编译报错 “no matching constructor”,往往是因为传了带引用返回的函数,或者参数类型不匹配(比如漏了 const)。
- 函数不能返回引用(
int& f()不行),但可以返回值、指针、void - 成员函数必须绑定对象实例,写法是
std::thread t(&MyClass::func, &obj, arg1, arg2) - 如果函数有 const 成员限定符,传入的对象也得是 const 引用或 const 指针
线程栈大小和异常传播:C++ 标准不管,靠平台和实现
标准库没提供设置栈大小的接口,std::thread 的默认栈尺寸取决于操作系统(Linux 通常是 8MB,Windows 约 1MB)。如果线程里递归太深或局部变量太大,会直接栈溢出,崩溃时甚至不抛异常。
另一个关键点:线程内未捕获的异常不会传播到主线程,而是直接调用 std::terminate()。
- 务必在线程函数最外层加
try/catch(...),至少打日志再退出 - 需要跨线程传错误信息?用
std::promise/std::future或共享状态(如std::atomic<bool></bool>+ 错误码) - 想控制栈大小?得绕开
std::thread,用平台 API(如 pthread_attr_setstacksize 或 Windows 的CreateThread)
事情说清了就结束:线程不是黑盒,但它的资源归属、参数生命周期、异常边界都比看起来更脆。稍不注意,就是静默崩溃或数据错乱。









