std::promise 和 std::future 是 C++ 异步编程中生产者 - 消费者模型的核心配对:前者作为 "结果生产者" 手动控制异步结果生成,后者作为 "结果消费者" 接收并获取结果;set_value(promise 成员)、wait_for/get(future 成员)是控制结果生产与消费的关键函数。以下是其核心逻辑、用法及示例
一、核心组件与关键函数
1. std::promise:异步结果的生产者
std::promise 是手动管理异步结果的载体,可在任意线程中设置结果或异常,并将其传递给绑定的 std::future。
-
核心特性:
- 不可拷贝,仅支持移动(需通过
std::move传递,如传给线程); - 与 std::future 一对一绑定,通过
get_future()获取关联的 future(该方法仅可调用一次)。
- 不可拷贝,仅支持移动(需通过
-
核心函数:
函数 功能 set_value(T&& val)为 promise 设置结果值,触发关联的 future 进入 "就绪状态";仅可调用一次 ,多次调用会抛出 std::future_error异常set_exception(std::exception_ptr e)为 promise 设置异常,后续 future.get()时会抛出该异常
2. std::future:异步结果的消费者
std::future 是异步结果的 "占位符",用于接收 promise 生成的结果,提供阻塞 / 定时等待、获取结果等能力。
-
核心函数:
函数 功能 get()阻塞至结果就绪,返回结果;仅可调用一次 ,调用后 future 变为无效( valid()返回 false);若 promise 设置了异常,此方法会抛出该异常wait_for(std::chrono::duration)定时阻塞等待结果,返回 std::future_status枚举值,避免主线程无限阻塞:-ready:结果已就绪,可调用get()获取-timeout:等待超时,结果仍未就绪-deferred:结果延迟执行(仅std::async使用std::launch::deferred策略时触 发,promise 绑定的 future 无此状态)valid()判断 future 是否有效(未调用 get()、未被移动)
二、关键注意事项
- 移动语义:std::promise 无拷贝构造函数,传递给线程等场景时必须使用
std::move,否则编译报错; - 单次操作限制:
promise::set_value仅可调用一次,future::get仅可调用一次,违反会触发异常或程序崩溃; - 避免悬空 promise:若 promise 析构前未调用
set_value/set_exception,其析构函数会设置 "broken promise" 异常,后续future.get()会抛出std::future_error; future_status::deferred:该状态仅针对std::async的延迟执行策略,promise 绑定的 future 不会返回此状态。
三、总结
- std::promise 作为 "生产者",通过
set_value/set_exception控制异步结果 / 异常的生成; - std::future 作为 "消费者",通过
wait_for定时等待结果状态,通过get阻塞获取最终结果; - 两者配对实现了线程间结果的安全传递,是 C++ 中 "手动控制异步结果" 的核心模式(区别于
std::async的自动异步),广泛应用于线程池、异步 IO、分布式任务等场景,可灵活控制结果的生产与消费时机。
四、使用示例
示例 1:基础用法(promise+future+set_value+get)
生产者线程生成结果,主线程阻塞获取结果
cpp
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
// 生产者线程:设置异步结果
void producer(std::promise<int> prom) {
// 模拟耗时任务(如数据库查询、网络请求)
std::this_thread::sleep_for(std::chrono::seconds(2));
prom.set_value(42); // 设置结果,触发future就绪(仅可调用一次)
}
int main() {
// 1. 创建promise并绑定future
std::promise<int> prom;
std::future<int> fut = prom.get_future();
// 2. 启动生产者线程(promise不可拷贝,需move)
std::thread prod_thread(producer, std::move(prom));
// 3. 主线程阻塞获取结果
std::cout << "等待生产者结果...\n";
int result = fut.get(); // 阻塞至set_value调用
std::cout << "获取到结果:" << result << "\n"; // 输出42
prod_thread.join();
return 0;
}
示例 2:非阻塞等待(wait_for + 循环检查)
主线程定时检查结果状态,避免无限阻塞:
cpp
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
void producer(std::promise<int> prom) {
std::this_thread::sleep_for(std::chrono::seconds(3));
prom.set_value(100);
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread prod_thread(producer, std::move(prom));
// 循环定时检查结果状态
while (true) {
std::future_status status = fut.wait_for(std::chrono::seconds(1));
if (status == std::future_status::ready) {
std::cout << "结果就绪!值:" << fut.get() << "\n"; // 输出100
break;
} else if (status == std::future_status::timeout) {
std::cout << "等待超时,结果仍未就绪...\n"; // 输出2次
}
}
prod_thread.join();
return 0;
}
示例 3:异常处理(set_exception+get 捕获)
生产者抛出异常,消费者捕获并处理:
cpp
#include <iostream>
#include <future>
#include <thread>
#include <stdexcept>
void producer_with_error(std::promise<int> prom) {
try {
// 模拟任务异常
throw std::runtime_error("数据库连接失败");
} catch (...) {
// 将异常传递给future
prom.set_exception(std::current_exception());
}
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread prod_thread(producer_with_error, std::move(prom));
// 消费结果并捕获异常
try {
int result = fut.get();
std::cout << "结果:" << result << "\n";
} catch (const std::exception& e) {
std::cout << "捕获异常:" << e.what() << "\n"; // 输出"数据库连接失败"
}
prod_thread.join();
return 0;
}