C++跨线程异常传递唯一标准方式是std::exception_ptr:先current_exception()捕获,再安全传递,最后rethrow_exception()重抛;推荐用std::promise/std::future封装,需确保自定义异常拷贝构造为noexcept且生命周期受控。

不能直接跨线程抛出异常。C++标准禁止从一个线程 throw 异常后由另一个线程 catch —— 因为每个线程有独立的调用栈,throw 只能在当前栈展开。
std::exception_ptr 是唯一标准跨线程传异常的机制
它不是“抛”,而是“捕获+传递+重抛”三步闭环:std::current_exception() 捕获当前线程的异常状态(内部堆分配一份副本),转成 std::exception_ptr 对象,再通过线程安全方式(如 std::queue、std::promise 或共享变量)传给目标线程;目标线程调用 std::rethrow_exception(ptr) 才真正触发本地异常流程。
-
std::exception_ptr是空的(比如初始化为nullptr)时调用rethrow_exception会直接调用std::terminate - 必须确保传递过程线程安全:若用裸指针或全局变量传
exception_ptr,需配std::mutex或改用std::promise/std::future -
std::promise::set_exception内部就封装了current_exception+ 安全传递,比手写更可靠
std::promise/std::future 是最常用且不易出错的封装
它把异常传递逻辑收进类型系统,避免手动管理 exception_ptr 生命周期和同步问题。适用于“启动一个异步任务,主线程等结果或错误”的典型场景。
- 子线程中:在
catch块里调用promise.set_exception(std::current_exception()) - 主线程中:调用
future.get(),若子线程设过异常,get()会立即rethrow_exception - 注意:同一个
std::promise只能调用一次set_value或set_exception,重复调用是未定义行为 - 不要在
std::promise析构前忘记调用set_*,否则future.get()会死锁
自定义异常类必须满足 no-throw 拷贝构造
std::rethrow_exception 在目标线程重建异常对象时,会调用原异常类型的拷贝构造函数。如果这个构造函数抛异常(比如内部 new 失败又没处理),整个重抛就失败,程序终止。
立即学习“C++免费学习笔记(深入)”;
- 继承
std::runtime_error等标准异常是安全的——它们的拷贝构造函数都标记为noexcept - 自定义类要显式声明:
MyException(const MyException&) noexcept,且所有成员拷贝都不能抛异常 - 避免在拷贝构造里做任何可能失败的操作(如打开文件、分配大内存)
真正容易被忽略的是:异常对象的生命周期完全依赖 std::exception_ptr 的持有者。只要还有一个 exception_ptr 指向它,底层堆对象就活着;一旦所有 exception_ptr 被销毁,对象才析构。这和普通智能指针类似,但没人帮你自动管理——传错作用域或提前释放,就会导致 rethrow_exception 行为未定义。


















