C++并发编程指南 std::promise 介绍与使用

文章目录

  • `std::promise`:异步编程中的结果契约
  • [作用一 1. std::promise` 在这里充当了线程间通信的安全契约!](#作用一 1. std::promise` 在这里充当了线程间通信的安全契约!)
      • [🚚 重构后的外卖流程(无全局变量):](#🚚 重构后的外卖流程(无全局变量):)
      • [📦 `std::promise` 的关键作用(参数传递版):](#📦 std::promise 的关键作用(参数传递版):)
      • [🔄 程序执行流程:](#🔄 程序执行流程:)
      • [🌟 为什么这样设计更好?](#🌟 为什么这样设计更好?)
  • [作用二 2. std::promise扮演着​​异步结果传递的中介​​角色,它连接了传统的回调式API和现代的基于future的调用方式](#作用二 2. std::promise扮演着异步结果传递的中介角色,它连接了传统的回调式API和现代的基于future的调用方式)
  • [`std::promise` 在快递服务中的核心作用](#std::promise 在快递服务中的核心作用)
    • [🔑 核心作用:将回调转换为future](#🔑 核心作用:将回调转换为future)
    • [🧩 具体实现中的关键点](#🧩 具体实现中的关键点)
      • [1. 生命周期管理 (`std::make_shared`)](#1. 生命周期管理 (std::make_shared))
      • [2. 回调适配器](#2. 回调适配器)
      • [3. Future返回](#3. Future返回)
    • [📊 与传统方式的对比](#📊 与传统方式的对比)
    • [🌟 `std::promise` 的核心价值](#🌟 std::promise 的核心价值)
    • [🏁 总结](#🏁 总结)
  • 传统方式也可以让用户知道状态,但是不够优雅
  • 传统方式的状态传递机制详解
  • [作用三 3.当多个线程需要协作完成一个任务,并且需要将部分结果传递回主线程时。](#作用三 3.当多个线程需要协作完成一个任务,并且需要将部分结果传递回主线程时。)
  • [团队协作项目:`std::promise` 在多线程协作中的作用](#团队协作项目:std::promise 在多线程协作中的作用)
    • [🧩 `std::promise` 在团队协作中的作用](#🧩 std::promise 在团队协作中的作用)
      • [1. ​**​任务成果传递机制​**​](#1. 任务成果传递机制)
      • [2. ​**​同步协调机制​**​](#2. 同步协调机制)
      • [3. ​**​一对一的成果交付​**​](#3. 一对一的成果交付)
      • [4. ​**​生命周期管理​**​](#4. 生命周期管理)
    • [📊 程序执行流程示例](#📊 程序执行流程示例)
    • [🌟 `std::promise` 的核心价值](#🌟 std::promise 的核心价值)
    • [🆚 与传统同步方式的对比](#🆚 与传统同步方式的对比)
    • [💡 总结](#💡 总结)
  • [作用四 4. 超时和异常处理的高级控制](#作用四 4. 超时和异常处理的高级控制)
  • [外卖订餐超时系统:`std::promise` 在超时和异常处理中的作用](#外卖订餐超时系统:std::promise 在超时和异常处理中的作用)
    • [🍕 `std::promise` 在超时和异常处理中的作用](#🍕 std::promise 在超时和异常处理中的作用)
      • [1. 超时控制机制](#1. 超时控制机制)
      • [2. 异常传递通道](#2. 异常传递通道)
      • [3. 状态管理](#3. 状态管理)
      • [4. 线程间通信](#4. 线程间通信)
    • [📊 程序执行示例](#📊 程序执行示例)
    • [⚠️ 重要注意事项](#⚠️ 重要注意事项)
    • [🌟 `std::promise` 的核心价值](#🌟 std::promise 的核心价值)
    • [🆚 与传统超时处理的对比](#🆚 与传统超时处理的对比)
    • [💡 总结](#💡 总结)

std::promise:异步编程中的结果契约

std::promise是C++11引入的异步编程工具,与std::future配合使用,用于在线程之间传递结果(值或异常)。它充当了一个​​异步结果的契约​ ​:一个线程(生产者)可以通过promise设置结果,而另一个线程(消费者)可以通过关联的future获取该结果。

核心作用

  1. ​结果传递契约​ ​:promisefuture共同构成一个契约,生产者通过promise.set_value()promise.set_exception()设置结果,消费者通过future.get()获取结果。

  2. ​线程间同步​​:自动处理线程间的同步,消费者在结果未就绪时会阻塞等待。

  3. ​异常传递​ ​:支持跨线程传递异常,生产者可以通过set_exception将异常传递给消费者。

  4. ​状态查询​ ​:提供wait_for()wait_until()方法,允许消费者查询结果状态或设置超时。

  5. ​多结果传递​ ​:每个promise对应一个future,适合一对一的异步结果传递。

适用场景

  1. ​分离结果生产与消费​ ​:生产者线程和消费者线程解耦,通过promise/future传递结果。

  2. ​封装回调为Future​ ​:将传统的基于回调的API转换为基于future的API。

  3. ​多线程协作​​:多个工作线程分别产生结果,主线程汇总结果。

  4. ​超时和异常处理​​:支持超时等待和跨线程异常传递。

接下来,我们将通过四个具体场景详细展示std::promise的作用。

作用一 1. std::promise` 在这里充当了线程间通信的安全契约!

cpp 复制代码
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

// 餐厅厨房(生产者)
void kitchen(std::promise<std::string>& order_promise) {
    std::cout << "厨师: 开始制作披萨...\n";
    std::this_thread::sleep_for(std::chrono::seconds(3)); // 制作披萨需要时间
    std::cout << "厨师: 披萨制作完成!准备送出\n";
    order_promise.set_value("热腾腾的披萨"); // 完成订单承诺
}

// 顾客(消费者)
void customer(std::future<std::string>&& order_future) {
    std::cout << "顾客: 下单了披萨,等待外卖...\n";
    
    // 等待外卖送达(这会阻塞直到订单完成)
    std::string food = order_future.get(); 
    
    std::cout << "顾客: 收到外卖 - " << food << "!可以开吃了\n";
}

int main() {
    // 创建订单承诺(就像外卖平台创建订单)
    std::promise<std::string> order_promise;
    
    // 顾客下单(获取订单凭证)
    auto order_future = order_promise.get_future();
    auto customer_thread = std::async(std::launch::async, customer, std::move(order_future));
    
    // 餐厅开始制作(传递订单承诺)
    std::this_thread::sleep_for(std::chrono::seconds(1));
    auto kitchen_thread = std::async(std::launch::async, kitchen, std::ref(order_promise));
    
    // 等待所有流程完成
    kitchen_thread.wait();
    customer_thread.wait();
    
    std::cout << "系统: 订单完成,流程结束\n";
    return 0;
}

🚚 重构后的外卖流程(无全局变量):

  1. ​创建订单​​:

    • 主函数(外卖平台)创建订单承诺 order_promise
    • 生成订单凭证 order_future(给顾客)
    • 保留订单承诺 order_promise(给餐厅)
  2. ​顾客下单​​:

    • 顾客线程接收​订单凭证​ (std::move(order_future))
    • 开始等待外卖 (order_future.get())
  3. ​餐厅制作​​:

    • 厨房线程接收​订单承诺​ (std::ref(order_promise))
    • 完成后标记订单完成 (set_value("热腾腾的披萨"))

📦 std::promise 的关键作用(参数传递版):

  1. ​订单契约​​:

    • promise 是餐厅和顾客之间的契约
    • 主函数(外卖平台)创建契约并分发给双方
  2. ​安全传递​​:

    • 通过 std::movefuture ​安全转移​给顾客
    • 通过 std::refpromise ​安全引用​给餐厅
  3. ​作用域控制​​:

    • promise 的生命周期由主函数管理
    • 当主函数结束时,所有资源自动清理
  4. ​线程安全通信​​:

    • 即使在不同线程,set_value()get() 也能安全同步
    • 不需要全局变量或额外锁机制

🔄 程序执行流程:

复制代码
(主函数创建订单承诺和凭证)
顾客: 下单了披萨,等待外卖...  // 顾客线程启动
(1秒后)
厨师: 开始制作披萨...        // 厨房线程启动
(3秒后)
厨师: 披萨制作完成!准备送出
顾客: 收到外卖 - 热腾腾的披萨!可以开吃了
系统: 订单完成,流程结束

🌟 为什么这样设计更好?

  1. ​避免全局状态​​:

    • 订单系统完全自包含在函数参数中
    • 不同订单可以并行处理(创建多个promise/future对)
  2. ​资源管理​​:

    • 订单完成后自动释放资源
    • 不会留下全局残留状态
  3. ​更符合现实​​:

    • 就像真实外卖平台:创建订单 → 分发给顾客和餐厅 → 完成后归档
    • 不需要全局可见的"订单中心"

这种模式在实际编程中更常见和安全,特别是在需要多个独立异步操作的系统中。std::promise 在这里充当了线程间通信的安全契约!

作用二 2. std::promise扮演着​​异步结果传递的中介​​角色,它连接了传统的回调式API和现代的基于future的调用方式

cpp 复制代码
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

// ================ 快递服务定义 ================
// 传统的快递服务(基于回调)
void send_package(const std::string& item, std::function<void(std::string)> delivery_callback) {
    std::thread([=] {
        std::cout << "快递员: 正在打包和运送 " << item << "..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(2)); // 运送时间
        delivery_callback(item + " 已送达收件人"); // 传统方式:电话通知
        }).detach();
}

// 使用promise将传统快递服务转换为现代追踪服务
std::future<std::string> track_package(const std::string& item) {
    // 创建快递追踪单(promise)
    auto tracking_promise = std::make_shared<std::promise<std::string>>();

    // 使用传统快递服务,但添加追踪功能
    send_package(item, [tracking_promise](std::string status) {
        tracking_promise->set_value(status); // 更新追踪状态
        });

    // 返回追踪单号(future)
    return tracking_promise->get_future();
}

// ================ 传统方式演示 ================
void traditional_way() {
    std::cout << "\n======= 传统快递服务(回调方式)=======\n";
    std::cout << "顾客: 寄送重要文件(使用传统服务)\n";

    // 使用传统快递服务
    send_package("重要文件", [](std::string status) {
        // 当快递员送达后,会打电话(调用回调函数)通知顾客
        std::cout << "顾客: 接到电话通知 - " << status << std::endl;
        });

    // 顾客在等待期间可以做其他事,但无法主动查询状态
    std::cout << "顾客: 等待快递中...(可以做其他事,但不知道何时送达)\n";

    // 模拟顾客处理其他工作
    for (int i = 1; i <= 4; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        std::cout << "顾客: 正在处理其他工作 " << i << "/4\n";
    }

    std::cout << "顾客: 工作完成,但不知道快递是否送达\n";
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 确保回调完成
}

// ================ 现代方式演示 ================
void modern_way() {
    std::cout << "\n======= 现代快递追踪服务(promise转换)=======\n";
    std::cout << "顾客: 寄送另一份文件(使用追踪服务)\n";

    // 使用现代追踪服务
    auto delivery_status = track_package("另一份文件");

    std::cout << "顾客: 追踪单已生成,可以继续工作...\n";

    // 顾客可以主动查询状态
    for (int i = 1; i <= 4; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));

        // 正确检查future状态
        auto status = delivery_status.wait_for(std::chrono::seconds(0));
        if (status == std::future_status::ready) {
            std::cout << "顾客: 查看追踪单 - 快递已送达!\n";
            break;
        }
        else {
            std::cout << "顾客: 查看追踪单 - 快递仍在运送中("
                << i * 500 << "毫秒)\n";
        }
    }

    // 最终获取送达状态(可能等待)
    std::cout << "顾客: 等待最终送达确认...\n";
    std::string result = delivery_status.get();
    std::cout << "顾客: 追踪系统显示 - " << result << std::endl;
}

// ================ 主函数 ================
int main() {
    // 演示传统快递服务方式
    traditional_way();

    // 演示现代追踪服务方式
    modern_way();

    std::cout << "\n======= 服务对比总结 =======\n";
    std::cout << "传统方式: 被动等待通知,无法主动查询状态\n";
    std::cout << "现代方式: 主动查询状态,随时了解进度\n";

    return 0;
}

std::promise 在快递服务中的核心作用

在这个快递服务场景中,std::promise 扮演着​​异步结果传递的中介​​角色,它连接了传统的回调式API和现代的基于future的调用方式。以下是它的具体作用和价值:

🔑 核心作用:将回调转换为future

  1. ​接口转换器​​:

    • std::promise 将传统的回调式API (send_package) 转换为现代future式API (track_package)
    • 调用者不再需要处理回调函数,而是获得一个可以直接查询的 future
  2. ​结果容器​​:

    • promise 作为一个"结果容器",保存快递服务的最终状态
    • 当快递送达时,通过 set_value() 将结果存入容器
  3. ​同步机制​​:

    • 当结果未就绪时,future.get() 会阻塞调用者
    • 当结果设置后,自动唤醒等待的线程
  4. ​线程间通信桥梁​​:

    • 在快递员线程(回调线程)和顾客线程(主线程)之间安全传递结果
    • 确保线程安全的数据传递

🧩 具体实现中的关键点

cpp 复制代码
std::future<std::string> track_package(const std::string& item) {
    // 1. 创建promise作为结果容器
    auto tracking_promise = std::make_shared<std::promise<std::string>>();
    
    // 2. 将回调转换为set_value操作
    send_package(item, [tracking_promise](std::string status) {
        tracking_promise->set_value(status); // 关键转换点
    });
    
    // 3. 返回future给调用者
    return tracking_promise->get_future();
}

1. 生命周期管理 (std::make_shared)

  • 使用 shared_ptr 确保 promise 在回调执行时仍然有效
  • 避免回调执行时 promise 已被销毁的问题

2. 回调适配器

cpp 复制代码
[tracking_promise](std::string status) {
    tracking_promise->set_value(status);
}
  • 这个lambda函数将回调参数转换为 promise.set_value()
  • 是传统回调与现代future之间的桥梁

3. Future返回

  • promise.get_future() 返回一个与promise关联的future
  • 调用者通过这个future获取最终结果

📊 与传统方式的对比

​特性​ ​传统方式(回调)​ ​现代方式(promise/future)​
​结果获取​ 被动等待回调通知 主动查询或等待结果
​状态可见性​ 不可见(黑盒) 可查询状态(就绪/未就绪)
​错误处理​ 难以统一处理 可通过 set_exception 统一处理
​接口复杂度​ 回调嵌套复杂 线性调用,代码清晰
​多任务处理​ 回调地狱风险 可组合多个future

🌟 std::promise 的核心价值

在这个快递服务场景中,std::promise 的核心价值在于:

  1. ​封装复杂性​​:

    • 将回调的复杂性隐藏在 track_package 函数内部
    • 对外提供简单的future接口
  2. ​提升可组合性​​:

    • future可以与其他future组合(如 when_all, when_any
    • 支持更复杂的异步工作流
  3. ​统一错误处理​​:

    • 如果快递丢失,可以通过 set_exception 报告错误
    • 调用者通过 future.get() 统一捕获异常
  4. ​时间解耦​​:

    • 结果生产(快递送达)和消费(顾客查询)时间完全分离
    • 双方不需要知道对方的存在或状态

🏁 总结

在这个快递服务场景中,std::promise 就像一个​​智能快递中转站​​:

  1. 接收快递员的通知(回调)
  2. 将通知转换为可查询的状态(future)
  3. 让顾客可以随时查询状态(get()wait_for()

它完美解决了传统回调模式的痛点:

  • 被动等待 → 主动查询
  • 状态不可见 → 状态可监控
  • 回调嵌套 → 线性调用

通过 std::promise,我们成功将传统的"电话通知"快递服务升级为现代的"在线追踪"系统,大大提升了用户体验和代码质量!

传统方式也可以让用户知道状态,但是不够优雅

传统方式的状态传递机制详解

在传统回调方式中,要将状态值传递到外部给顾客知道,我们需要使用一些额外的机制。以下是几种常见的方法:

方法1:使用共享变量(最简单但不安全)

cpp 复制代码
// 全局变量存储状态(不推荐)
std::string global_delivery_status;

void traditional_way_unsafe() {
    std::cout << "\n======= 传统方式(使用全局变量)=======\n";
    std::cout << "顾客: 寄送重要文件\n";
    
    // 使用传统快递服务
    send_package("重要文件", [](std::string status) {
        // 将状态存储到全局变量
        global_delivery_status = status;
        std::cout << "顾客: 接到电话通知 - " << status << std::endl;
    });
    
    // 顾客可以尝试查询状态(但不可靠)
    std::cout << "顾客: 等待快递中...\n";
    
    // 模拟顾客定期检查状态
    for (int i = 1; i <= 4; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        if (!global_delivery_status.empty()) {
            std::cout << "顾客: 检查到快递状态: " << global_delivery_status << std::endl;
        } else {
            std::cout << "顾客: 检查快递状态 - 暂无更新\n";
        }
    }
}

问题:

  • ​线程不安全​:多个线程可能同时访问全局变量
  • ​状态不一致​:顾客可能看到部分更新的状态
  • ​生命周期问题​:如果顾客在回调前结束,状态可能无效

方法2:使用原子标志位(稍好但仍有限)

cpp 复制代码
#include <atomic>

std::atomic<bool> delivery_completed(false);
std::string delivery_status; // 需要配合互斥锁

void traditional_way_atomic() {
    std::cout << "\n======= 传统方式(使用原子标志)=======\n";
    std::cout << "顾客: 寄送重要文件\n";
    
    // 重置状态
    delivery_completed = false;
    
    send_package("重要文件", [](std::string status) {
        delivery_status = status;
        delivery_completed = true; // 设置完成标志
        std::cout << "顾客: 接到电话通知 - " << status << std::endl;
    });
    
    std::cout << "顾客: 等待快递中...\n";
    
    for (int i = 1; i <= 4; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        if (delivery_completed.load()) {
            std::cout << "顾客: 检查到快递已送达: " << delivery_status << std::endl;
        } else {
            std::cout << "顾客: 检查快递状态 - 运送中\n";
        }
    }
}

改进:

  • 使用原子变量确保标志位的线程安全
  • 避免部分状态可见性问题

仍存在的问题:

  • 状态字符串本身不是线程安全的
  • 只能知道是否完成,不知道中间状态
  • 无法区分不同快递的状态

方法3:使用条件变量(完整解决方案)

cpp 复制代码
#include <mutex>
#include <condition_variable>

class DeliveryTracker {
    std::mutex mtx;
    std::condition_variable cv;
    std::string status;
    bool completed = false;
    
public:
    void update_status(const std::string& new_status) {
        std::lock_guard<std::mutex> lock(mtx);
        status = new_status;
        completed = true;
        cv.notify_all();
    }
    
    std::string wait_for_status() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this] { return completed; });
        return status;
    }
    
    bool try_get_status(std::string& out_status) {
        std::lock_guard<std::mutex> lock(mtx);
        if (completed) {
            out_status = status;
            return true;
        }
        return false;
    }
};

void traditional_way_proper() {
    std::cout << "\n======= 传统方式(使用条件变量)=======\n";
    std::cout << "顾客: 寄送重要文件\n";
    
    auto tracker = std::make_shared<DeliveryTracker>();
    
    send_package("重要文件", [tracker](std::string status) {
        tracker->update_status(status);
        std::cout << "顾客: 接到电话通知 - " << status << std::endl;
    });
    
    std::cout << "顾客: 等待快递中...\n";
    
    // 主动查询状态
    for (int i = 1; i <= 4; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        
        std::string current_status;
        if (tracker->try_get_status(current_status)) {
            std::cout << "顾客: 检查到快递状态: " << current_status << std::endl;
        } else {
            std::cout << "顾客: 检查快递状态 - 运送中\n";
        }
    }
    
    // 最终等待结果
    std::string final_status = tracker->wait_for_status();
    std::cout << "顾客: 最终快递状态: " << final_status << std::endl;
}

完整解决方案特点:

  1. ​线程安全​​:

    • 使用互斥锁保护共享状态
    • 条件变量提供安全的等待/通知机制
  2. ​状态查询​​:

    • try_get_status() 允许非阻塞查询
    • wait_for_status() 提供阻塞等待
  3. ​封装性​​:

    • 状态管理封装在 DeliveryTracker 类中
    • 调用者无需关心同步细节
  4. ​生命周期管理​​:

    • 使用 shared_ptr 确保回调时对象仍然有效

🌟 为什么 std::promise 是更好的解决方案

对比传统方式的各种解决方案,std::promise 提供了更优雅的实现:

cpp 复制代码
// 现代方式(使用promise)
std::future<std::string> modern_way() {
    auto prom = std::make_shared<std::promise<std::string>>();
    
    send_package("文件", [prom](std::string status) {
        prom->set_value(status);
    });
    
    return prom->get_future();
}

// 使用
auto future = modern_way();
// 非阻塞查询
if (future.wait_for(0s) == std::future_status::ready) {
    auto status = future.get();
}
// 阻塞等待
auto status = future.get();

std::promise 的优势:

  1. ​内置同步机制​​:

    • 自动处理线程安全
    • 无需手动管理锁和条件变量
  2. ​标准接口​​:

    • 统一的 future 接口
    • 与其他标准库组件兼容
  3. ​异常安全​​:

    • 支持 set_exception()
    • 异常自动传递
  4. ​超时支持​​:

    • wait_for()wait_until() 内置支持
    • 无需额外计时器
  5. ​资源管理​​:

    • 自动管理关联状态
    • 避免资源泄漏

💡 结论

在传统回调方式中,要将状态传递到外部,我们需要:

  1. 使用共享变量(不安全)
  2. 添加同步机制(锁、条件变量)
  3. 手动管理生命周期

std::promise 本质上是一个封装了:

  • 值存储
  • 互斥锁
  • 条件变量
  • 异常处理
    的完整状态管理容器。

它提供了:

cpp 复制代码
// 相当于传统方式中的:
class PromiseEquivalent {
    std::mutex mtx;
    std::condition_variable cv;
    std::optional<T> value;
    std::exception_ptr exception;
    bool ready = false;
    
public:
    void set_value(T val) {
        std::lock_guard lock(mtx);
        value = std::move(val);
        ready = true;
        cv.notify_all();
    }
    
    T get() {
        std::unique_lock lock(mtx);
        cv.wait(lock, [this]{ return ready; });
        if (exception) std::rethrow_exception(exception);
        return *value;
    }
    
    // 还有 wait_for, wait_until 等...
};

因此,std::promise 是现代C++中处理异步状态传递的标准解决方案,它封装了传统方式中需要手动实现的所有复杂同步机制,提供了安全、简洁的接口。

作用三 3.当多个线程需要协作完成一个任务,并且需要将部分结果传递回主线程时。

cpp 复制代码
#include <iostream>
#include <future>
#include <vector>
#include <thread>
#include <chrono>

// 团队成员完成任务
void team_member_work(int member_id, std::shared_ptr<std::promise<int>> task_promise) {
    std::cout << "成员" << member_id << ": 开始执行任务...\n";
    // 模拟任务执行时间,每个成员执行时间不同(member_id+1秒)
    std::this_thread::sleep_for(std::chrono::seconds(member_id + 1));

    // 假设任务结果是成员ID乘以100(模拟任务成果)
    int task_result = member_id * 100;
    std::cout << "成员" << member_id << ": 完成任务,成果 = " << task_result << std::endl;

    // 将任务成果报告给项目经理(设置promise的值)
    task_promise->set_value(task_result);
}

int main() {
    const int team_size = 3; // 团队有3个成员
    std::vector<std::future<int>> task_results; // 存储每个成员的任务结果(future)
    std::vector<std::thread> team_members; // 存储团队成员(线程)

    // 分配任务给每个成员
    for (int i = 0; i < team_size; ++i) {
        // 为每个成员创建一个任务承诺(promise)
        auto member_promise = std::make_shared<std::promise<int>>();
        // 获取与承诺关联的future,用于之后获取结果
        task_results.push_back(member_promise->get_future());

        // 创建成员线程,并传递任务承诺
        team_members.emplace_back(team_member_work, i, member_promise);
    }

    std::cout << "项目经理: 任务已分配,等待团队成员报告...\n";

    // 项目经理等待所有成员的任务结果
    int project_total = 0;
    for (int i = 0; i < team_size; ++i) {
        // 获取每个成员的任务结果(如果结果未就绪,这里会阻塞等待)
        int result = task_results[i].get();
        project_total += result;
        std::cout << "项目经理: 收到成员" << i << "的任务成果: " << result << std::endl;
    }

    // 等待所有成员线程结束
    for (auto& member : team_members) {
        member.join();
    }

    std::cout << "项目总成果: " << project_total << std::endl;
    return 0;
}

团队协作项目:std::promise 在多线程协作中的作用

我将使用"团队完成项目任务"的生活场景重构代码,并解释 std::promise 的作用:

cpp 复制代码
#include <iostream>
#include <future>
#include <vector>
#include <thread>
#include <chrono>

// 团队成员完成任务
void team_member_work(int member_id, std::shared_ptr<std::promise<int>> task_promise) {
    std::cout << "成员" << member_id << ": 开始执行任务...\n";
    // 模拟任务执行时间,每个成员执行时间不同(member_id+1秒)
    std::this_thread::sleep_for(std::chrono::seconds(member_id + 1));
    
    // 假设任务结果是成员ID乘以100(模拟任务成果)
    int task_result = member_id * 100;
    std::cout << "成员" << member_id << ": 完成任务,成果 = " << task_result << std::endl;
    
    // 将任务成果报告给项目经理(设置promise的值)
    task_promise->set_value(task_result);
}

int main() {
    const int team_size = 3; // 团队有3个成员
    std::vector<std::future<int>> task_results; // 存储每个成员的任务结果(future)
    std::vector<std::thread> team_members; // 存储团队成员(线程)

    // 分配任务给每个成员
    for (int i = 0; i < team_size; ++i) {
        // 为每个成员创建一个任务承诺(promise)
        auto member_promise = std::make_shared<std::promise<int>>();
        // 获取与承诺关联的future,用于之后获取结果
        task_results.push_back(member_promise->get_future());
        
        // 创建成员线程,并传递任务承诺
        team_members.emplace_back(team_member_work, i, member_promise);
    }

    std::cout << "项目经理: 任务已分配,等待团队成员报告...\n";
    
    // 项目经理等待所有成员的任务结果
    int project_total = 0;
    for (int i = 0; i < team_size; ++i) {
        // 获取每个成员的任务结果(如果结果未就绪,这里会阻塞等待)
        int result = task_results[i].get();
        project_total += result;
        std::cout << "项目经理: 收到成员" << i << "的任务成果: " << result << std::endl;
    }

    // 等待所有成员线程结束
    for (auto& member : team_members) {
        member.join();
    }

    std::cout << "项目总成果: " << project_total << std::endl;
    return 0;
}

🧩 std::promise 在团队协作中的作用

在这个场景中,std::promise 充当了​​团队成员与项目经理之间的任务成果交付契约​​:

1. ​​任务成果传递机制​

  • 每个团队成员持有一个 promise 对象

  • 完成任务后,通过 set_value() 提交成果

  • 项目经理通过关联的 future 获取成果

    // 成员提交成果
    task_promise->set_value(task_result);

    // 项目经理获取成果
    int result = task_results[i].get();

2. ​​同步协调机制​

  • future.get() 会阻塞直到结果就绪
  • 确保项目经理在汇总结果时,所有成员的任务已经完成
  • 自动处理线程间的同步,无需手动使用锁或条件变量

3. ​​一对一的成果交付​

  • 每个成员有自己的 promise-future
  • 成果独立传递,互不干扰
  • 项目经理可以按任意顺序收集成果

4. ​​生命周期管理​

  • 使用 std::make_shared 确保 promise 在成员线程执行期间保持有效
  • 避免悬空指针或资源提前释放的问题

📊 程序执行流程示例

复制代码
成员0: 开始执行任务...
成员1: 开始执行任务...
成员2: 开始执行任务...
项目经理: 任务已分配,等待团队成员报告...
成员0: 完成任务,成果 = 0
项目经理: 收到成员0的任务成果: 0
成员1: 完成任务,成果 = 100
项目经理: 收到成员1的任务成果: 100
成员2: 完成任务,成果 = 200
项目经理: 收到成员2的任务成果: 200
项目总成果: 300

🌟 std::promise 的核心价值

在这个团队协作场景中,std::promise 提供了:

  1. ​成果交付契约​​:

    • 明确每个成员需要交付的成果
    • 确保成果只被交付一次
  2. ​线程间通信桥梁​​:

    • 在成员线程和主线程之间安全传递结果
    • 自动处理同步问题
  3. ​解耦任务执行和结果收集​​:

    • 成员只负责执行任务和设置结果
    • 项目经理只负责收集和汇总结果
    • 双方不需要知道对方的实现细节
  4. ​可扩展性​​:

    • 可以轻松添加更多成员
    • 支持不同执行时间的任务
    • 结果收集顺序灵活

🆚 与传统同步方式的对比

​特性​ ​传统方式(锁/条件变量)​ ​Promise/Future方式​
​同步机制​ 需要手动管理锁和条件变量 自动处理同步
​代码复杂度​ 复杂,容易出错 简洁,直观
​结果传递​ 需要共享变量 通过future直接传递
​错误处理​ 复杂,容易遗漏 可通过set_exception统一处理
​可读性​ 低,业务逻辑与同步代码混合 高,业务逻辑清晰分离

💡 总结

在这个团队协作场景中,std::promise 就像项目经理给每个团队成员的任务承诺书:

  1. 成员接受任务时获得承诺书(promise)
  2. 完成任务后在承诺书上签字确认(set_value)
  3. 项目经理收集签字的承诺书(future.get())
  4. 最终汇总所有成果

这种方式大大简化了多线程协作的复杂性,使代码更清晰、更安全、更易于维护,完美体现了 std::promise 在多线程协作中的价值!

作用四 4. 超时和异常处理的高级控制

外卖订餐超时系统:std::promise 在超时和异常处理中的作用

我将使用"外卖订餐超时处理"的生活场景重构代码,并解释 std::promise 的作用:

cpp 复制代码
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <stdexcept>

// 模拟餐厅准备食物(可能超时)
std::future<std::string> prepare_food(const std::string& dish) {
    auto order_promise = std::make_shared<std::promise<std::string>>();
    
    // 启动厨房线程准备食物
    std::thread([order_promise, dish] {
        try {
            std::cout << "厨房: 开始制作 " << dish << "...\n";
            
            // 模拟制作时间(5秒)
            std::this_thread::sleep_for(std::chrono::seconds(5));
            
            // 检查订单是否已被取消(超时)
            if (order_promise->get_future().wait_for(std::chrono::seconds(0)) != 
                std::future_status::timeout) {
                std::cout << "厨房: " << dish << " 制作完成\n";
                order_promise->set_value(dish + " 已准备好");
            } else {
                std::cout << "厨房: " << dish << " 制作完成但订单已取消\n";
            }
        } catch (...) {
            // 捕获厨房异常(如食材用完)
            order_promise->set_exception(std::current_exception());
        }
    }).detach();
    
    return order_promise->get_future();
}

int main() {
    std::cout << "顾客: 下单了披萨,等待中...\n";
    auto food_future = prepare_food("披萨");
    
    // 设置顾客等待时间为3秒
    if (food_future.wait_for(std::chrono::seconds(3)) == std::future_status::timeout) {
        std::cout << "顾客: 等待超时,取消订单!\n";
        
        // 可以在这里执行取消逻辑
        // 注意:厨房线程可能仍在运行
    } else {
        try {
            std::string result = food_future.get();
            std::cout << "顾客: " << result << ",可以开吃了!\n";
        } catch (const std::exception& e) {
            std::cout << "顾客: 遇到问题 - " << e.what() << ",申请退款!\n";
        }
    }
    
    // 等待足够时间让厨房线程完成(实际应用中需要更好的资源管理)
    std::this_thread::sleep_for(std::chrono::seconds(3));
    return 0;
}

🍕 std::promise 在超时和异常处理中的作用

在这个外卖订餐场景中,std::promise 充当了​​订单状态管理器​​的角色,它提供了:

1. 超时控制机制

  • ​顾客端超时检测​​:

    cpp 复制代码
    if (food_future.wait_for(std::chrono::seconds(3)) == std::future_status::timeout) {
        std::cout << "顾客: 等待超时,取消订单!\n";
    }

    顾客可以设置最大等待时间,超时后取消等待

  • ​厨房端超时检查​​:

    cpp 复制代码
    if (order_promise->get_future().wait_for(std::chrono::seconds(0)) != 
        std::future_status::timeout) {
        // 只有订单未被取消时才设置值
    }

    厨房在完成食物后检查订单是否已被取消

2. 异常传递通道

  • ​捕获厨房异常​​:

    cpp 复制代码
    catch (...) {
        order_promise->set_exception(std::current_exception());
    }

    捕获厨房可能发生的任何异常(如食材用完、设备故障)

  • ​顾客端异常处理​​:

    cpp 复制代码
    try {
        std::string result = food_future.get();
    } catch (const std::exception& e) {
        std::cout << "顾客: 遇到问题 - " << e.what() << ",申请退款!\n";
    }

    顾客统一处理成功和失败情况

3. 状态管理

  • ​订单状态保存​ ​:
    promise 保存订单的最终状态(完成、超时或异常)

  • ​状态查询接口​ ​:
    future 提供状态查询方法(wait_for)和结果获取方法(get

4. 线程间通信

  • ​厨房线程到顾客线程​
    安全传递订单状态,无需共享变量或锁
  • ​顾客线程到厨房线程​
    通过 future 状态间接通知厨房订单是否被取消

📊 程序执行示例

情况1:超时(3秒等待 < 5秒制作)

c 复制代码
顾客: 下单了披萨,等待中...
厨房: 开始制作 披萨...
顾客: 等待超时,取消订单!
厨房: 披萨 制作完成但订单已取消

情况2:成功(若将等待时间改为6秒)

c 复制代码
顾客: 下单了披萨,等待中...
厨房: 开始制作 披萨...
厨房: 披萨 制作完成
顾客: 披萨 已准备好,可以开吃了!

情况3:厨房异常

c 复制代码
顾客: 下单了披萨,等待中...
厨房: 开始制作 披萨...
顾客: 遇到问题 - 食材用完了,申请退款!

⚠️ 重要注意事项

  1. ​后台线程管理​​:

    • 使用 detach() 的线程在超时后仍在运行
    • 实际应用中需要更完善的取消机制(如原子标志位)
  2. ​资源泄漏风险​​:

    • 示例中简单使用 sleep 等待线程结束
    • 生产环境应使用 std::jthread 或手动管理线程
  3. ​取消协作​​:

    • 厨房检查 future 状态判断是否被取消
    • 非强制中断,而是协作式取消
  4. ​异常安全​​:

    • set_exception 确保任何异常都能传递给顾客
    • 避免线程因异常而无声无息地退出

🌟 std::promise 的核心价值

在这个超时处理场景中,std::promise 提供了:

  1. ​统一的异步结果处理​​:

    • 成功结果、超时状态和异常统一通过同一个接口处理
  2. ​超时检测能力​​:

    • wait_for 提供精确的超时控制
    • 避免无限期阻塞
  3. ​异常安全传递​​:

    • 跨线程传递异常,保持调用栈信息
    • 统一错误处理入口
  4. ​取消协作机制​​:

    • 通过 future 状态间接通知生产者取消
    • 生产者可以选择是否继续完成工作

🆚 与传统超时处理的对比

​特性​ ​传统方式​ ​Promise/Future方式​
​超时检测​ 需要手动计时器 内置 wait_for
​异常处理​ 难以跨线程传递 自动跨线程传递
​取消机制​ 复杂,需要共享标志 通过future状态协作
​代码复杂度​
​资源安全​ 容易泄漏 自动管理结果状态

💡 总结

在这个外卖订餐场景中,std::promise 就像智能订单系统:

  1. 顾客下单时创建订单(promise)
  2. 厨房接受订单开始制作
  3. 顾客可以设置等待超时
  4. 厨房完成后更新订单状态
  5. 任何问题(超时或异常)都能妥善处理

通过 std::promise,我们实现了:

  • 精确的超时控制
  • 安全的异常传递
  • 协作式取消机制
  • 统一的异步结果处理

完美模拟了现实中的外卖超时处理场景,展示了 std::promise 在高级同步控制中的强大能力!

相关推荐
ganshenml18 小时前
【Android】两个不同版本的jar放进一个工程打成aar会有问题么?
android·java·jar
灰什么鱼18 小时前
项目同时打war和jar两种包
java·jar
爱隐身的官人18 小时前
Linux配置Java/JDK(解决Kali启动ysoserial.jar JRMPListener报错)暨 Kali安装JAVA8和切换JDK版本的详细过程
java·linux·kali
semicolon_hello18 小时前
C++中 optional variant any 的使用
开发语言·c++
草莓熊Lotso19 小时前
《测试视角下的软件工程:需求、开发模型与测试模型》
java·c++·测试工具·spring·软件工程
代码小将19 小时前
java泛型笔记
java·笔记
报错小能手19 小时前
C++笔记(基础)string基础
开发语言·c++·笔记
青草地溪水旁19 小时前
从“手机拆修”看懂POD与非POD的区别
c++
先知后行。20 小时前
Qt 网络编程
开发语言·网络·qt