std::thread构造后必须detach或join,否则析构时调用std::terminate导致程序崩溃;推荐RAII封装自动join;传参需注意值拷贝与引用陷阱,引用须用std::ref包装;多线程同步优先用std::lock_guard而非手动锁;带返回值任务宜用std::async+std::future。

std::thread 构造后必须 detach 或 join
创建 std::thread 对象后,如果既没调用 join() 也没调用 detach(),对象析构时会触发 std::terminate() —— 程序直接崩溃,且无堆栈提示。这是新手最常踩的坑。
- 用
join()表示主线程等待该线程结束,适合需要同步结果的场景 - 用
detach()表示“放生”,线程后台运行,但此后无法再控制或获取返回值 - 推荐封装:把
std::thread包进 RAII 类(如scoped_thread),在析构中自动join(),避免裸用
传递参数到线程函数时注意值拷贝与引用陷阱
std::thread 构造时对参数默认按值拷贝,即使你传的是引用,也会拷一份副本。想真正传引用,必须用 std::ref() 显式包装;传指针则没问题,但要确保所指对象生命周期覆盖线程执行期。
- 错误写法:
std::thread t(func, my_vec);→my_vec被拷贝,线程里改的是副本 - 正确传引用:
std::thread t(func, std::ref(my_vec)); - 传局部变量地址?危险!比如
int x = 42; std::thread t(func, &x);→ 主线程函数返回后x已销毁,线程访问野指针
std::mutex + std::lock_guard 是最安全的互斥方案
手动调用 lock()/unlock() 容易遗漏、异常跳过或重复解锁。用 std::lock_guard 自动管理锁的生命周期,构造即加锁,析构即释放,无论是否异常都能保证安全。
- 别用
std::mutex::try_lock()做常规保护,它只适合“尝试获取、失败就走”的非阻塞场景 - 多个互斥量一起锁?用
std::lock()+std::adopt_lock避免死锁,而不是分别lock() -
std::recursive_mutex允许同一线程多次加锁,但性能差、易掩盖设计问题,尽量不用
std::async 和 std::future 适合带返回值的异步任务
如果线程要算个结果并返回,比手写 std::thread + 共享变量 + 手动同步更简洁可靠的方式是 std::async。它自动管理线程生命周期,并通过 std::future 获取结果或异常。
立即学习“C++免费学习笔记(深入)”;
-
std::async(std::launch::async, func, args...)强制新开线程;std::launch::deferred则延迟到get()时才执行(类似惰性求值) -
future.get()是阻塞调用,且只能调用一次;重复调用会抛std::future_error - 如果任务可能抛异常,
get()会重新抛出原异常,这点比裸线程+标志位健壮得多
shared_ptr 的引用计数,也可能因原子性缺失引发未定义行为。写完记得用 ThreadSanitizer(Clang/GCC 支持)跑一遍,它能抓到绝大多数竞态。









