C/C++内置库函数(3):future、promise的用法

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()、未被移动)

二、关键注意事项

  1. 移动语义:std::promise 无拷贝构造函数,传递给线程等场景时必须使用std::move,否则编译报错;
  2. 单次操作限制:promise::set_value仅可调用一次,future::get仅可调用一次,违反会触发异常或程序崩溃;
  3. 避免悬空 promise:若 promise 析构前未调用set_value/set_exception,其析构函数会设置 "broken promise" 异常,后续future.get()会抛出std::future_error
  4. 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;
}
相关推荐
weixin_440730503 小时前
java数组整理笔记
java·开发语言·笔记
Dillon Dong3 小时前
从C到Simulink: 使用STM32硬件支持包后为什么还不支持PC仿真ARM建模程序
c语言·stm32·simulink
Thera7773 小时前
状态机(State Machine)详解:原理、优缺点与 C++ 实战示例
开发语言·c++
linux开发之路3 小时前
C++高性能日志库开发实践
c++·c++项目·后端开发·c++新特性·c++校招
niucloud-admin3 小时前
java服务端——controller控制器
java·开发语言
刻BITTER4 小时前
在TRAE 上安装PlatformIO
c++·单片机·嵌入式硬件·arduino
永远都不秃头的程序员(互关)4 小时前
C++动态数组实战:从手写到vector优化
c++·算法
夏幻灵4 小时前
JAVA基础:基本数据类型和引用数据类型
java·开发语言
水力魔方4 小时前
武理排水管网模拟分析系统应用专题5:模型克隆与并行计算
数据库·c++·算法·swmm
cike_y4 小时前
Spring-Bean的作用域&Bean的自动装配
java·开发语言·数据库·spring