std::thread 构造后必须 join 或 detach,否则析构时调用 std::terminate() 导致程序崩溃,错误信息如“Resource deadlock avoided”或“terminate called without an active exception”。

std::thread 构造后必须 join 或 detach
不处理的 std::thread 对象在析构时会调用 std::terminate(),程序直接崩溃,错误信息是 std::thread::~thread(): Resource deadlock avoided 或更常见的 terminate called without an active exception。
常见错误是写完 std::thread t{func} 就结束作用域,没管它:
void bad_example() {
std::thread t([]{ std::cout << "hello\n"; });
} // t 析构 → crash
- 用
t.join()等待线程结束(适合需要同步结果的场景) - 用
t.detach()让线程后台运行(之后不能再控制或等待,且需确保所捕获变量生命周期足够长) - 更安全的做法:把
std::thread包进 RAII 类型里,比如封装成scoped_thread,或直接用std::jthread(C++20,构造时自动 join)
传参到 thread 时要小心值传递和引用传递
std::thread 的参数默认按值拷贝,即使你写了 &x,也会被当作右值转发,导致意外的拷贝或绑定失效。
比如想在线程里修改外部变量 int a = 0:
立即学习“C++免费学习笔记(深入)”;
int a = 0;
std::thread t([&a]{ a = 42; }); // ✅ 正确:lambda 捕获引用
// 但下面这句不行:
std::thread t2(func, a); // ❌ func 收到的是 a 的副本
std::thread t3(func, std::ref(a)); // ✅ 用 std::ref 包装才传引用
- 函数对象参数一律按值移动/拷贝,不会自动推导引用语义
- 要传引用,必须显式用
std::ref(x)或std::cref(x) - 传入局部变量地址(如
&local_var)而没保证生命周期,detach 后极易访问野指针
共享数据不加锁就会出现未定义行为
多个线程读写同一变量(哪怕只是 counter++),只要没同步机制,就是未定义行为——不是“偶尔错”,而是编译器可任意优化、CPU 可乱序执行、结果完全不可预测。
典型现象:循环 10000 次,两个线程各加 1,最后 counter 却远小于 20000。
- 基础方案:用
std::mutex+std::lock_guard保护临界区 - 简单计数可用
std::atomic<int></int>,比锁轻量,但注意++是原子的,而a = a + 1不是 - 别用
volatile替代同步——它只禁用编译器优化,不管 CPU 重排也不提供原子性
thread_local 变量每个线程一份,但初始化时机容易误判
thread_local 看似简单,实际行为依赖线程启动时机和变量定义位置。
全局或命名空间作用域的 thread_local 变量,在**首次访问该线程中该变量时**初始化;函数内定义的,则每次进入函数都可能触发(如果该线程还没初始化过)。
- 不能假设所有线程的
thread_local变量在main()开始就已构造好 - 若初始化过程抛异常,该线程后续访问会再次尝试初始化(可能反复失败)
- 销毁顺序不确定,跨线程使用其地址做回调时,要确认目标线程还没退出
多线程下最麻烦的从来不是怎么开线程,而是变量谁在什么时候构造、谁在什么时候消亡、谁还拿着指向它的指针。这些细节一漏,问题就藏得深、复现难。










