使用 std::async 时须显式指定 std::launch::async 并避免析构中无条件调用 get();条件变量需双重检查退出标志;std::thread 析构前必须 join();jthread 虽自动 join 但中断状态不可重置。

std::async 启动线程时必须注意 m_fuThread_.get() 的阻塞风险
直接用 std::async 在构造函数里拉起线程看似简洁,但若未显式管理其返回的 std::future,析构时调用 m_fuThread_.get() 会**强制阻塞等待完成**——哪怕线程还在条件变量上死等。这会导致析构卡住,尤其在线程尚未被唤醒、m_bReady_ 仍为 false 时。
实操建议:
- 避免在析构中无条件调用
get();改用wait_for()加超时判断,防止无限挂起 - 确保线程函数内部有明确退出路径:不能只靠
while(true)+wait,必须检查m_bExit_并主动 break -
std::async默认使用std::launch::deferred或async策略,务必显式指定std::launch::async,否则可能延迟执行甚至不启新线程
std::condition_variable::wait 必须配合原子变量做双重检查
只靠 cv_.wait(lock, []{ return stop_.load(); }) 是不够的。如果通知发生在 wait 进入阻塞前(即“丢失唤醒”),线程会永远等下去。正确做法是:先检查退出标志,再进入等待,并在唤醒后再次检查。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 主线程调用
Notify()后,监控线程没响应,程序无法退出 - 线程刚启动就收到通知,但因未进入
wait而跳过,后续又陷入无条件等待
实操建议:
- 把退出逻辑拆成两层:
while (!stop_.load()) { cv_.wait_for(lock, 100ms); }—— 即使唤醒丢失,也能靠超时轮询兜底 - 条件变量的谓词 lambda 必须捕获
this并读取stop_.load(),不能用普通bool成员 - 唤醒前务必先锁住
mutex并更新stop_,否则存在可见性竞争
类析构时线程未退出导致 std::thread 析构抛异常
std::thread 对象被销毁时,若底层线程仍在运行,会自动调用 std::terminate()。这是硬性规则,和是否用了 std::async 无关——只要线程句柄还活着,就必须 join() 或 detach()。
使用场景:
- 传感器监控类生命周期短于线程运行时间(如临时对象、频繁创建销毁)
- 单元测试中快速构造/析构,容易暴露未 join 问题
实操建议:
- 在析构函数开头就设置
stop_.store(true)并notify_one(),然后调用join() - 不要依赖
std::async的std::future自动管理线程生命周期;它不等于std::thread,但同样需要你确保线程结束 - 加一层 RAII 封装:用
std::unique_ptr<:thread></:thread>持有,析构前手动join(),比裸指针更可控
C++20 jthread 是最简方案,但要注意中断状态不可重置
std::jthread 自动在析构时调用 join(),且内置中断机制,比手写 atomic + cv 更少出错。但它不是万能的:一旦线程被中断,get_stop_token().stop_requested() 就永远为 true,且无法重置。
性能 / 兼容性影响:
- MSVC 19.30+、GCC 10.2+、Clang 12+ 才完整支持;旧项目升级需评估编译器链
- 中断点仅对
std::this_thread::sleep_for、std::condition_variable::wait等标准阻塞函数生效,自定义系统调用或第三方库阻塞不会响应 - 若线程内有长耗时计算循环,仍需手动插入
if (stoken.stop_requested()) break;
最容易被忽略的一点:中断机制依赖线程函数参数接收 std::stop_token,漏传或忽略检查,jthread 就退化为普通 std::thread。


















