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;
}
相关推荐
嘿BRE1 小时前
【C++】智能指针
c++
代码游侠1 小时前
学习笔记——GPIO按键与中断系统
c语言·开发语言·arm开发·笔记·嵌入式硬件·学习·重构
R-sz1 小时前
app登录接口实现,基于JWT的APP登录认证系统实现方案
java·开发语言·python
楼田莉子1 小时前
Linux学习之库的原理与制作
linux·运维·服务器·c++·学习
Elieal1 小时前
@Api 系列注解
java·开发语言
保护我方头发丶1 小时前
hard_link.bat(个人用)
c语言
Remember_9931 小时前
【数据结构】深入理解Map和Set:从搜索树到哈希表的完整解析
java·开发语言·数据结构·算法·leetcode·哈希算法·散列表
浅念-1 小时前
C++第一课
开发语言·c++·经验分享·笔记·学习·算法
Coding茶水间1 小时前
基于深度学习的路面裂缝检测系统演示与介绍(YOLOv12/v11/v8/v5模型+Pyqt5界面+训练代码+数据集)
开发语言·人工智能·深度学习·yolo·目标检测·机器学习
你爱写程序吗(新H)1 小时前
基于单片机的洗衣机控制系统设计 单片机洗衣机控制(设计+文档)
c语言·汇编·单片机·嵌入式硬件·matlab