C++中std::promise的使用详解和实战示例

在 C++ 中,std::promise 头文件提供的一种用于线程间通信的机制,它和 std::future 配合使用,让一个线程可以向另一个线程发送一个值(或异常),常用于异步任务或线程的结果传递。


一句话理解:

std::promise 用于 “承诺” 在未来某个时刻提供一个值,这个值可以通过绑定的 std::future 来获取。


核心组成:

  • std::promise:设置一个类型为 T 的值。
  • std::future:从另一个线程获取该值。

基本语法:

#include 

std::promise<T> p;
std::future<T> f = p.get_future(); // 获取与 promise 绑定的 future

// 线程 A 中设置值
p.set_value(value);

// 线程 B 中获取值(阻塞式)
T result = f.get(); // 等待 promise 设置值

简单示例:一个线程计算值,另一个线程获取值

#include 
#include 
#include 

void compute_square(std::promise<int> result_promise, int x) {
    int result = x * x;
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时
    result_promise.set_value(result); // 设置计算结果
}

int main() {
    std::promise<int> p;
    std::future<int> f = p.get_future(); // 获取 future

    std::thread t(compute_square, std::move(p), 10); // 启动线程传递 promise

    std::cout << "等待结果...\n";
    int result = f.get(); // 阻塞直到 promise 设置值
    std::cout << "结果是: " << result << std::endl;

    t.join();
    return 0;
}

输出(大致):

等待结果...
结果是: 100

注意事项:

1. std::promise一次性使用 的:

  • set_value() 只能调用一次。
  • 如果你没有设置值就销毁了 promise,则 future.get() 会抛出 std::future_error

2. std::future 也只能 get() 一次:

  • 再次调用将抛出异常。

示例:异常传播(set_exception

void fail_computation(std::promise<int> p) {
    try {
        throw std::runtime_error("发生错误");
    } catch (...) {
        p.set_exception(std::current_exception()); // 捕获并传递异常
    }
}

int main() {
    std::promise<int> p;
    std::future<int> f = p.get_future();

    std::thread t(fail_computation, std::move(p));

    try {
        int result = f.get(); // 将抛出异常
    } catch (const std::exception& ex) {
        std::cerr << "捕获到异常: " << ex.what() << std::endl;
    }

    t.join();
}

输出:

捕获到异常: 发生错误

常见用途:

场景 用法说明
多线程通信 一个线程计算,另一个线程获取
线程任务返回值 类似 std::packaged_task
异常跨线程传递 set_exception() 实现
用于线程池任务同步 promise + future 成为接口

实战示例

下面搭建一个 包含 std::promise 的线程池框架,用于异步任务提交与结果获取(future)。这类系统适合用来:

  • 并发处理多个任务;
  • 允许任务有返回值;
  • 自动管理线程的生命周期。

目标功能

支持提交任意可调用对象(函数、Lambda、std::bind、成员函数)
返回 std::future,可以获取任务结果
自动复用固定数量的线程
使用 std::promise 实现结果传递


完整线程池代码(基于 C++17)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class ThreadPool {
public:
    explicit ThreadPool(size_t num_threads);
    ~ThreadPool();

    // 提交任务并返回 future
    template<typename Func, typename... Args>
    auto submit(Func&& func, Args&&... args) -> std::future<decltype(func(args...))>;

private:
    // 工作线程循环体
    void worker();

    std::vector<std::thread> workers_;
    std::queue<std::function<void()>> tasks_; // 包装好的任务队列

    std::mutex queue_mutex_;
    std::condition_variable condition_;
    std::atomic<bool> stop_;
};

ThreadPool::ThreadPool(size_t num_threads) : stop_(false) {
    for (size_t i = 0; i < num_threads; ++i)
        workers_.emplace_back([this]() { this->worker(); });
}

ThreadPool::~ThreadPool() {
    stop_ = true;
    condition_.notify_all(); // 通知所有线程退出

    for (std::thread& t : workers_)
        if (t.joinable()) t.join();
}

void ThreadPool::worker() {
    while (!stop_) {
        std::function<void()> task;
        {
            std::unique_lock<std::mutex> lock(queue_mutex_);
            condition_.wait(lock, [this]() { return stop_ || !tasks_.empty(); });

            if (stop_ && tasks_.empty())
                return;

            task = std::move(tasks_.front());
            tasks_.pop();
        }
        task(); // 执行任务
    }
}

template<typename Func, typename... Args>
auto ThreadPool::submit(Func&& func, Args&&... args) -> std::future<decltype(func(args...))> {
    using ReturnType = decltype(func(args...));

    // 创建 promise 和 future
    auto task_promise = std::make_shared<std::promise<ReturnType>>();
    std::future<ReturnType> result_future = task_promise->get_future();

    // 包装任务:捕获参数并执行函数,设置结果到 promise
    auto task = std::make_shared<std::packaged_task<ReturnType()>>(
        std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
    );

    {
        std::lock_guard<std::mutex> lock(queue_mutex_);
        if (stop_) throw std::runtime_error("ThreadPool has been stopped");

        // 添加任务:捕获 packaged_task 并调用
        tasks_.emplace([task]() { (*task)(); });
    }

    condition_.notify_one(); // 通知工作线程

    return result_future;
}

示例用法

int slow_add(int a, int b) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return a + b;
}

int main() {
    ThreadPool pool(4); // 启动 4 个线程

    std::future<int> f1 = pool.submit(slow_add, 1, 2);
    std::future<int> f2 = pool.submit(slow_add, 3, 4);

    std::cout << "等待计算结果...\n";

    std::cout << "结果1: " << f1.get() << std::endl;
    std::cout << "结果2: " << f2.get() << std::endl;

    return 0;
}

核心点说明

组件 说明
std::promise 用于设置值(由线程池设置)
std::future 调用方等待结果
std::packaged_task 包装任务 + 管理 promise
std::bind/std::function 统一封装任意任务
std::condition_variable 唤醒空闲线程执行任务
std::atomic 控制是否停止线程池

小结:

操作 方法
绑定 future get_future()
设置结果 set_value(val)
设置异常 set_exception(std::current_exception())
获取结果(阻塞) future.get()

你可能感兴趣的:(C++中std::promise的使用详解和实战示例)