std::promise与std::future通过共享状态单向传值:promise调用set_value()设值,future调用get()取值;二者必须同源,get()/set_value()均只能调用一次,否则抛future_error。

std::promise 和 std::future 是怎么配对传值的
它们不是“在线程间直接传值”,而是通过共享一个同步状态(shared state)来实现值的单向传递:一个线程用 std::promise 设置值,另一个线程用对应的 std::future 等待并获取它。这个共享状态由 std::promise 构造时隐式创建,调用 get_future() 获取绑定的 std::future 实例。
关键点在于:两者必须来自同一对象(或移动后关联),不能手动“传递” future 或 promise 对象本身来建立连接。
-
std::promise只能调用一次set_value()、set_exception()或set_exception_at_thread_exit();重复调用会抛出std::future_error(错误码为std::future_errc::promise_already_satisfied) -
std::future的get()只能调用一次,之后再调用会抛出std::future_error(错误码为std::future_errc::no_state) - 如果 promise 被析构而未设置值,对应 future 的
get()会抛出std::future_error(std::future_errc::broken_promise)
如何安全地把 promise/future 交给不同线程
不能直接在线程函数参数里传引用(比如 std::promise<int>&</int>),因为线程启动后原 promise 可能已析构。正确做法是用 std::move 转移所有权,或用 std::shared_ptr 包裹(仅当需多消费者时才考虑后者)。
最常见且推荐的模式是:在主线程构造 std::promise,调用 get_future() 得到 std::future,然后把 std::promise 移动进子线程,把 std::future 留在主线程(或传给其他等待线程)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t([p = std::move(prom)]() mutable {
// 子线程里 set_value
p.set_value(42);
});
t.detach(); // 或 t.join()
// 主线程等待
int result = fut.get(); // 阻塞直到 set_value 被调用
</int></int>
为什么 std::packaged_task 更适合带参数的异步任务
如果你需要传参执行函数,并在线程外获取返回值,std::promise + std::thread 手动配对容易出错(比如忘记 move、异常没捕获)。此时应优先用 std::packaged_task —— 它内部自动管理 promise/future 绑定,还支持异常传播。
-
std::packaged_task构造时接受可调用对象,调用它等价于执行目标函数并自动调用set_value(或set_exception) - 它的
get_future()返回的std::future会自动接收返回值或异常,无需手动 try/catch 包裹 - 注意:
std::packaged_task不可拷贝,只能移动;调用后其get_future()仍有效
std::packaged_task<int int> task([](int a, int b) { return a + b; });
std::future<int> fut = task.get_future();
std::thread t(std::move(task), 10, 32);
t.join();
int sum = fut.get(); // 得到 42,若 task 内抛异常,这里 rethrow
</int></int>
std::async 什么时候比手动 promise/future 更省事
当你只需要“启动一个异步操作并稍后取结果”,且不关心具体线程管理,std::async 是最简方案。它自动创建 std::packaged_task + 线程(或延迟执行),返回 std::future。
但要注意默认策略:std::async 不加策略参数时行为未指定(可能延迟执行),必须显式传 std::launch::async 才保证立即新线程运行:
- 漏写
std::launch::async可能导致get()调用时才同步执行(即“懒加载”),破坏你对并发的预期 - 如果函数无返回值,返回的是
std::future<void></void>,仍需调用get()等待完成(否则析构时可能阻塞) - 不保存
std::future对象会导致其析构时阻塞等待完成(这是很多人踩坑的地方)
所以这行代码很危险:std::async(std::launch::async, []{ do_work(); }); —— future 临时对象一构造完就析构,线程被强制同步等待。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










