std::future和std::packaged_task,std::promise,std::async

本文介绍了C++中的异步编程工具std::future、std::promise、std::packaged_task和std::async的使用。std::promise用于承诺数据,std::future等待并获取数据;std::packaged_task将任务包装,返回结果;std::async则提供了一种简便的异步执行方式,支持并行执行或延迟执行。文中给出了相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看到源码中有很多地方使用了future,packaged_task,上网搜搜,加上自己理解,使用一下,让自己看代码的时候能更加方便。

1.std::future和std::promise
std::future顾名思义是等待,希望的意思,std::promise顾名思义是承诺,则我们可以理解成,一个给与承诺,一个等待承诺。
通过建立一个承诺者std::promise,一个等待承诺者std::futurestd::future是每一个承诺者std::promise中所带有的,通过std::future<int> dataFuture = dataPromise.get_future();,建立一个std::future,然后通过调用dataFuture.get()进行等待,此时是阻塞的,直到线程thPromise调用了dataPromise.set_value(10),将数据放进了承诺者中,则此时dataFuture.get(),得到数据,得到的值就是set_value中的数据。
参考代码如下:

#include <iostream>
#include <future>
int main()
{
    std::promise<int> dataPromise;                                  //建立一个承诺
    std::future<int> dataFuture = dataPromise.get_future();         //每个承诺的等待者
    std::thread thPromise([&dataPromise]
    {
        std::cout << "Enter thPromise
<think>首先,用户的问题是:我想理解 C++ 中 std::packaged_taskstd::promise区别及使用场景。具体是:请问C++ std::packaged_task vs std::promise 区别 使用场景。 我需要根据系统级指令来回答: - 所有行内数学表达式使用 $...$ 格式。但在这个上下文中,可能没有数学表达式,主要是C++代码概念。 - 独立公式使用 $$...$$,但这里可能不适用。 - LaTeX语法正确:确保任何代码或特殊格式正确。 - 使用中文回答:整个回答必须用中文。 - 生成相关问题:在回答结束后,添加一个相关问题部分。 - 引用段落末尾添加引用标识:如[^1]。用户提供了引用[1]到[4],我需要参考这些,但用户说“上述引用仅供参考”,所以我应该基于这些引用内容来支持我的回答,但不要直接复制。引用是: - [^1]: 关于std::packaged_task的使用场景示例代码。 - [^2]: 关于std::packaged_taskstd::promise的简介代码案例。 - [^3]: 关于C++异步操作:std::future, std::async, std::packaged_task, std::promise。 - [^4]: 关于std::future, std::promise, std::packaged_task, std::async,包括异常处理。 回答结构: - 结构清晰,逐步解决问题。 - 保证真实可靠,基于C++知识。 内容大纲: 1. **介绍**:简要介绍std::packaged_taskstd::promise。 2. **区别**:详细说明两者的区别。 - std::packaged_task:用于包装可调用对象,并提供一个future来获取结果。 - std::promise:用于在某个线程中设置值,并在其他线程中通过future获取。 3. **使用场景**: - std::packaged_task:当你想异步执行一个任务并获取结果时使用。 - std::promise:当你想在某个线程中显式设置一个值或异常时使用。 4. **代码示例**:提供简单的代码片段来说明。 5. **总结**:强调关键区别。 6. **相关问题**:生成2-3个相关问题。 使用引用:在回答中,当引用特定内容时,添加引用标识。例如,提到std::packaged_task的包装功能时,引用[^2]。 确保回答是中文。 最终输出格式: - 回答内容。 - 相关问题部分:以“§§相关问题§§”开头,然后列出问题。 现在,构建回答。</think>### C++ 中 std::packaged_taskstd::promise区别及使用场景 在 C++ 并发编程中,`std::packaged_task` `std::promise` 都是用于管理异步操作结果的重要工具,它们与 `std::future` 结合使用,但设计目的使用方式有显著区别。下面我将逐步解释两者的核心概念、区别及典型使用场景,确保内容真实可靠,基于 C++11 及以上标准。回答中会引用相关资源来支持关键点,并在引用段落末尾添加标识符(如[^1])。 #### 1. **核心概念介绍** - **`std::packaged_task`**: 这是一个类模板,用于包装任何可调用对象(如函数、lambda 表达式或函数对象),并将其与一个 `std::future` 关联。当你调用 `std::packaged_task` 对象时,它会执行包装的任务,并将结果(或异常)存储到关联的 future 中,以便其他线程可以获取。本质上,它提供了一种“任务封装”机制,简化了异步任务的启动结果获取[^2]。 - 示例代码: ```cpp #include <future> #include <iostream> int add(int a, int b) { return a + b; } int main() { std::packaged_task<int(int, int)> task(add); // 包装函数 std::future<int> result = task.get_future(); // 获取关联的 future std::thread t(std::move(task), 2, 3); // 在新线程执行任务 t.join(); std::cout << "Result: " << result.get() << std::endl; // 输出: Result: 5 return 0; } ``` - **`std::promise`**: 这也是一个类模板,用于在某个线程中显式设置一个值(或异常),并通过关联的 `std::future` 在其他线程中获取。与 `std::packaged_task` 不同,`std::promise` 不直接包装任务,而是提供一个“手动控制”的机制,允许你在任意点设置结果,适用于更灵活的异步场景[^2][^3]。 - 示例代码: ```cpp #include <future> #include <iostream> #include <thread> void set_value(std::promise<int> prom) { prom.set_value(42); // 在线程中设置值 } int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); // 获取关联的 future std::thread t(set_value, std::move(prom)); // 传递 promise 到线程 t.join(); std::cout << "Value: " << fut.get() << std::endl; // 输出: Value: 42 return 0; } ``` #### 2. **主要区别** 两者的核心区别在于**封装方式****控制粒度**: - **任务封装 vs. 值设置**: - `std::packaged_task` 专注于**封装整个任务**:它将一个可调用对象(如函数)包装起来,并自动管理结果的存储。调用任务后,结果自动传递给 future,无需手动干预[^2][^3]。 - `std::promise` 专注于**手动设置值或异常**:它不绑定具体任务,而是允许你在代码中任意位置调用 `set_value()` 或 `set_exception()` 来传递结果。这提供了更高的灵活性,但需要开发者手动管理[^3][^4]。 - **使用复杂性**: - `std::packaged_task` 更简单:适用于“执行一个任务并获取结果”的场景,减少了样板代码。例如,在任务队列中,它可以轻松提交任务并获取 future[^1]。 - `std::promise` 更底层:适用于需要精细控制的场景,比如在回调函数或事件驱动代码中设置值,或者处理异常时手动抛出异常到 future[^4]。 - **异常处理**: - 两者都支持异常传播:如果任务中抛出异常,`std::packaged_task` 会自动捕获并存储到 future 中;`std::promise` 则需要显式调用 `set_exception()`。例如,引用[^4] 中展示了 `std::async` 的异常处理,但原理类似:异常会保存在 future 中,通过 `get()` 获取时重新抛出。 - `std::packaged_task` 异常示例: ```cpp std::packaged_task<int()> task([]() { throw std::runtime_error("error"); return 0; }); std::future<int> fut = task.get_future(); std::thread t(std::move(task)); t.join(); try { fut.get(); } catch (const std::exception& e) { /* 处理异常 */ } ``` - `std::promise` 异常示例: ```cpp std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t([&prom]() { try { throw std::runtime_error("error"); } catch (...) { prom.set_exception(std::current_exception()); } }); t.join(); try { fut.get(); } catch (const std::exception& e) { /* 处理异常 */ } ``` - **性能与开销**: - `std::packaged_task` 通常有较低的开销,因为它直接绑定任务,减少了间接调用。 - `std::promise` 可能更轻量,但需要更多手动代码,适合高频或定制化场景。 #### 3. **典型使用场景** 基于引用资源[^1][^2][^3][^4],以下是两者的常见应用场景: - **`std::packaged_task` 的使用场景**: - **任务队列系统**:当需要将多个任务提交到线程池并异步获取结果时,`std::packaged_task` 是理想选择。例如,在 GUI 应用中,后台计算任务的结果通过 future 返回到主线程[^1]。 - **简化异步调用**:如果你有一个现有函数,想在新线程中运行并获取结果,使用 `std::packaged_task` 可以避免手动管理 promise future 的复杂性[^3]。 - **结合 `std::thread` 或 `std::async`**:它常作为 `std::async` 的底层实现,提供更直接的控制。 - **`std::promise` 的使用场景**: - **跨线程值传递**:当需要在不同线程间手动设置值,如回调函数或事件处理中。例如,在一个网络请求线程中,收到响应后通过 `set_value()` 通知主线程[^2][^3]。 - **异常处理与取消操作**:在复杂错误处理中,`std::promise` 允许显式设置异常或取消标志,提供更细粒度的控制。引用[^4] 中的示例展示了如何在任务失败时传播异常。 - **与第三方库集成**:当调用非 C++ 标准库(如 C 库)时,`std::promise` 可以封装回调,将结果传递回 C++ 线程。 #### 4. **总结** - **关键区别**:`std::packaged_task` 是“任务导向”的,自动封装可调用对象并管理结果;`std::promise` 是“值导向”的,提供手动设置结果的灵活性。选择时,优先用 `std::packaged_task` 简化任务执行,用 `std::promise` 处理需要显式控制的场景[^1][^3]。 - **最佳实践**:在 C++ 并发编程中,两者常与 `std::future` `std::async` 结合使用。例如,`std::async` 内部可能使用 `std::packaged_task` 来实现,而 `std::promise` 则用于定制化需求。始终确保资源安全(如使用 `std::move` 避免拷贝)异常处理[^4]。 通过合理选择工具,可以提升代码的可读性性能。如果您有具体代码案例,我可以进一步分析优化方案。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值