什么是 std::promise ?常见面试题有哪些?
std::promise 是什么?
std::promise 可以把一个值或异常“承诺”给未来(std::future)。你在一个线程里设置结果,另一个线程通过对应的 future 获取。它相当于手动搭建 future 的生产者端,而不像 std::async 那样自动绑定。
关键接口
std::promise<int> p;
std::future<int> f = p.get_future(); // 取得匹配的 future
p.set_value(42); // 将结果写入 promise
// 或 p.set_exception(std::make_exception_ptr(std::runtime_error("oops")));
get_future():只能调用一次,返回与promise绑定的future。set_value():把结果写入,唤醒等待future.get()的线程。set_exception():传递异常,future.get()会抛出该异常。- 析构前若未设置值/异常,
promise会自动调用set_exception(std::future_error),避免 future 永远等待。
示例:手动把结果传给 future
#include
#include
#include
void worker(std::promise<int> p) {
try {
int result = 21 * 2;
p.set_value(result); // 成功路径
} catch (...) {
p.set_exception(std::current_exception()); // 出错路径
}
}
int main() {
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread t(worker, std::move(p));
std::cout << "result = " << f.get() << "
"; // 这里阻塞等 worker 调用 set_value
t.join();
}
使用场景
- 自定义异步任务:你想自己控制线程创建、任务分发,但仍需要
future结果。 - 与回调式 API 结合:回调里调用
promise::set_value,把数据“推”给等待的 future。 - 异常传递:在后台任务中抛异常时,通过
set_exception让主线程get()重新抛出。
注意事项
promise不可复制(只能 move),确保只有一个生产者。- 必须设置结果:否则
future.get()会收到std::future_error(broken promise)。 set_value()只能调用一次,否则抛future_error。- 如果只是需要简单异步执行并返回结果,优先用
std::async;只有在需要更细粒度控制时才使用promise/future组合。
逐行解读:
#include:引入std::promise/std::future定义。#include:后面用std::cout输出。#include:创建std::thread需要。
5-11. void worker(std::promise:后台线程执行的函数。
- 6-9:尝试执行主体逻辑。这里计算
21 * 2,得到 42,并调用p.set_value(result)把结果写入 promise,这会唤醒等待该结果的future。 - 10:若任何异常被捕获,就通过
p.set_exception(std::current_exception())把异常塞进 promise,这样future.get()会在主线程抛出同样的异常。
14-21. int main():主线程入口。
- 15:创建
std::promise,用来在另一个线程设置p; int结果。 - 16:
std::future取得与f = p.get_future(); p关联的 future。注意get_future()只能调用一次。 - 18:
std::thread t(worker, std::move(p));启动线程执行 worker,并把 promise 所有权移动进去(promise 不可复制)。 - 20:
std::cout << "result = " << f.get() << " ";主线程在这里阻塞,直到 worker 调用set_value(或set_exception)。如果 worker 成功,get()返回 42 并打印;如果 worker 抛异常,get()会重新抛出。 - 21:
t.join();等待子线程结束,确保资源正确回收。
总结:这个程序演示如何手动用 std::promise / std::future 在线程间传递结果或异常。
这里的 future 和 promise 的关系?
std::promise是“生产者端”,负责向共享状态写入一个int结果或异常。std::future是“消费者端”,通过get_future()从 promise 派生,引用同一个共享状态。- worker 拿到 promise,调用
set_value(result)(或set_exception),将结果写入共享状态。 - main 拿到 future,调用
f.get()等待 promise 写完;共享状态准备好时,get()返回结果或抛异常。
因此:promise 负责“写结果”,future 负责“读结果”,两者成对绑定,共享同一份状态并实现跨线程传递。
面试中 c++ 的 promise 相关的高频面试题有哪些?
C++ std::promise 高频面试题 + 答案
-
std::promise和std::future的关系是什么?
promise负责向共享状态写入结果或异常;future从同一共享状态读取。promise::get_future()绑定一对 promise/future,跨线程传递值。 -
promise::set_value和set_value_at_thread_exit有何区别?set_value立即把结果写入共享状态。set_value_at_thread_exit延迟到当前线程退出时才写入,常用于确保线程退出前完成某些操作。
-
何时会抛出
std::future_error(broken promise)?
若promise在设置值/异常前就被销毁,或set_value/set_exception调用多次,future.get()会抛出future_error,提示 promise 被破坏。 -
为什么
promise不能复制?如何在线程间传递?
为避免多个生产者竞争同一共享状态,promise只允许 move(不可拷贝)。要在线程间传递需使用std::move(p)。 -
如何用
promise传递异常?
在捕获异常后调用promise::set_exception(std::current_exception())。future.get()会重新抛出,调用方可在主线程处理。 -
promise和其他类型有区别吗?
没有返回值时,用promise,调用set_value()(无参数)表示完成,future等待完成。常用于只表示“成功/失败”的异步信号。::get()
std::promise用在“异步任务没有返回值,只需要告诉别人“完成了/失败了”的场景。和其他promise的区别仅在“有没有要传的值”:
| 类型 | set_value 写法 | future.get() 行为 |
|---|---|---|
std::promise | p.set_value(42); | 返回 int 结果 |
std::promise | p.set_value("done"); | 返回 std::string |
std::promise | p.set_value();(无参数) | 只阻塞直到完成,不返回任何值 |
本质上,promise 只是告诉 future:“我已经完成了(或失败了)”,适合那种“只需要同步,不需要数据”的情况,比如:
std::promise<void> p;
std::future<void> f = p.get_future();
std::thread worker([&p] {
// ... 做一些事 ...
p.set_value(); // 告诉 future:完成
});
f.get(); // 只等完成(无返回值),若 worker 调 set_exception 会在这里抛
worker.join();
除去“没有返回值”这一点外,行为(异常传递、broken promise 等)和其他类型完全一致。
-
与
std::async相比,何时需要promise?
当你想手动管理任务启动地点(自建线程或回调)、或需将现有异步 API(如回调)包装成future,就用 promise。async适合简单“函数→future”场景。 -
promise可以多次set_value吗?
不行。共享状态只允许写入一次结果(或异常)。再次调用会抛future_error。 -
promise在析构时必须已经设置结果吗?
是。如果到析构还没 set,会自动set_exception(broken promise),防止future.get()永远阻塞。 -
怎么把
std::promise与回调式 API 结合?
常见模式:在主线程创建 promise/future,把 promise 传入回调,当 API 完成时在回调内set_value(),主线程等待future即可。这是构建“future 化”封装的常见手法。











