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;
}
相关推荐
2501_921649492 小时前
亚太股票数据API:日股、韩股、新加坡股票、印尼股票市场实时行情,实时数据API-python
开发语言·后端·python·websocket·金融
喵了meme2 小时前
Linux学习日记18:线程的分离
linux·运维·c语言·学习
chaodaibing2 小时前
【Java】一个批量更新插入数据到MySQL的工具类
java·开发语言·mysql
在坚持一下我可没意见2 小时前
Spring 后端安全双剑(上篇):JWT 无状态认证 + 密码加盐加密实战
java·服务器·开发语言·spring boot·后端·安全·spring
乾元2 小时前
从命令行到自动诊断:构建 AI 驱动的故障树与交互式排障机器人引言
运维·开发语言·网络·人工智能·华为·自动化
deng-c-f2 小时前
C/C++内置库函数(6):C++中类什么时候使用静态变量
开发语言·c++
2301_789015623 小时前
C++:模板进阶
c语言·开发语言·汇编·c++
互亿无线明明3 小时前
在 Go 项目中集成国际短信能力:从接口调试到生产环境的最佳实践
开发语言·windows·git·后端·golang·pycharm·eclipse
噔噔噔噔@3 小时前
详细介绍Python+Pytest+BDD+Playwright,用FSM打造高效测试框架
开发语言·python·pytest