std::thread析构时若joinable()为真会调用std::terminate()导致程序崩溃;必须显式join()或detach(),且detached线程不得访问已销毁的局部资源。

std::thread 是最直接的创建方式
用 std::thread 启动一个新线程,核心就是把函数(或可调用对象)传给它构造。注意:线程启动后必须明确管理生命周期,否则程序可能崩溃。
- 传入普通函数:直接写函数名,如
std::thread t(func) - 传入带参数的函数:参数按值或引用传递,但引用需用
std::ref(x)包裹,否则会尝试拷贝 - 传入 lambda:捕获方式要小心,
[&]捕获局部变量时,原函数返回后线程访问这些变量就未定义 - 必须调用
t.join()或t.detach(),否则std::thread析构时会调用std::terminate()
join 和 detach 的选择取决于线程用途
两者本质是解决“谁负责等待线程结束”这个问题。选错会导致程序卡死、资源泄漏或访问已销毁内存。
-
t.join():当前线程阻塞,直到t执行完;适合需要结果、或必须等任务完成再继续的场景 -
t.detach():把线程设为后台运行,和主线程解耦;但之后无法再控制或同步它,且所有局部变量必须确保在线程结束前仍有效 - 常见错误:在局部作用域创建
std::thread对象却没join或detach,比如函数末尾忘记处理,直接触发终止
避免数据竞争:别让多个线程同时读写同一变量
裸用全局变量或传入同一个对象指针,极易引发未定义行为。C++ 不会自动加锁,必须显式同步。
- 最常用的是
std::mutex+std::lock_guard:在临界区前加锁,作用域结束自动释放 - 不要手动调用
mutex.lock()/unlock(),容易漏掉 unlock 导致死锁 - 如果多个线程只读不写,共享 const 数据是安全的;一旦有写操作,就必须保护
- 注意:
std::cout本身不是线程安全的,多线程混着打日志会出现字符交错,建议每个线程单独缓冲再输出,或统一用锁保护
更安全的替代方案:std::async 配合 std::future
如果你只是想“异步执行一个函数并取回结果”,std::async 比裸 std::thread 更省心,它自动管理线程资源和生命周期。
- 默认启动策略是
std::launch::async(一定新开线程),也可以指定std::launch::deferred(延迟执行,类似惰性求值) - 返回
std::future,调用.get()会阻塞直到结果就绪;若已计算完则立即返回 - 注意:
std::future析构时若未调用.get(),且对应异步任务是async策略,会阻塞等待完成——这容易被忽略,导致意外卡住 - 不适用于需要长期运行、频繁通信的线程;它更适合“发个任务,拿个结果”的一次性操作
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











