C++11中future,promise,packaged_task和async介绍

本文介绍了C++11中引入的std::future和std::promise等并发编程工具的使用方法,包括它们如何帮助简化线程间的通信和同步,以及std::async、std::packaged_task等功能。

  为什么C++11引入std::future和std::promise?c++11创建了线程以后,我们不能直接从thread.join()得到结果,必须定义一个变量,在线程执行时,对这个变量赋值,然后执行join(),过程相对繁琐。

  thread库提供了future用来访问异步操作的结果。std::promise用来包装一个值将数据和future绑定起来,为获取线程函数中的某个值提供便利,取值是间接通过promise内部提供的future来获取的,也就是说promise的层次比future高。 

#include “stdafx.h”




#include <iostream> #include <type_traits> #include <future> #include <thread> using namespace std; int main() { std::promise< int> promiseParam; std::thread t([](std::promise< int>& p) { std::this_thread::sleep_for(std::chrono::seconds( 10)); // 线程睡眠10s p.set_value_at_thread_exit( 4); // }, std:: ref (promiseParam)); std::future< int> futureParam = promiseParam.get_future(); auto r = futureParam. get(); // 线程外阻塞等待 std::cout << r << std::endl; return 0 ; }

  上述程序执行到futureParam.get()时,有两个线程,新开的线程正在睡眠10s,而主线程正在等待新开线程的退出值,这个操作是阻塞的,也就是说std::future和std::promise某种程度也可以做为线程同步来使用。

  std::packaged_task包装一个可调用对象的包装类(如function,lambda表达式(C++11之lambda表达式),将函数与future绑定起来。std::packaged_task与std::promise都有get_future()接口,但是std::packaged_task包装的是一个异步操作,而std::promise包装的是一个值。

#include “stdafx.h”




#include <iostream> #include <type_traits> #include <future> #include <thread> using namespace std; int main() { std::packaged_task< int()> task([]() { std::this_thread::sleep_for(std::chrono::seconds( 10)); // 线程睡眠10s return 4 ; }); std::thread t1(std:: ref (task)); std::future< int> f1 = task.get_future(); auto r = f1. get(); // 线程外阻塞等待 std::cout << r << std::endl; return 0 ; }

  而std::async比std::promise, std::packaged_task和std::thread更高一层,它可以直接用来创建异步的task,异步任务返回的结果也保存在future中。std::async的原型:

async( std::launch policy, Function&& f, Args&&... args );

  std::launch policy有两个,一个是调用即创建线程(std::launch::async),一个是延迟加载方式创建线程(std::launch::deferred),当掉使用async时不创建线程,知道调用了future的get或者wait时才创建线程。之后是线程函数和线程参数。

#include “stdafx.h”




#include <iostream> #include <future> #include <thread> int main() { // future from a packaged_task std::packaged_task< int()> task([]() { std::cout << “ packaged_task started ” << std::endl; return 7; }); // wrap the function std::future< int> f1 = task.get_future(); // get a future std::thread(std::move(task)).detach(); // launch on a thread // future from an async() std::future< int> f2 = std::async(std::launch::deferred, []() { std::cout << “ Async task started ” << std::endl; return 8 ; }); // future from a promise std::promise< int> p; std::future< int> f3 = p.get_future(); std::thread([&p] { p.set_value_at_thread_exit( 9 ); }).detach(); f1.wait(); f2.wait(); f3.wait(); std::cout << “ Done!\nResults are: ” << f1. get() << ‘ ‘ << f2. get() << ‘ ‘ << f3. get() << ‘ \n ’ ; }

 

<think>好的,我现在要介绍一下C++中的packaged_task。首先,我需要理解用户的需求。用户之前询问了std::launch::deferred,现在转向packaged_task,可能是想了解与异步任务处理相关的组件。我需要确保回答的结构清晰,符合之前的格式,并且用中文准确解释概念。 首先,packaged_task是什么?它是一个类模板,用于包装可调用对象(如函数、lambda),并将其执行结果与future关联起来。这样允许异步获取结果。需要std::async、std::promise做比较,指出差异。 接下来,核心特性。可能需要分点说明,比如封装任务、绑定future、手动控制执行。每个点需要具体解释,比如封装任务是指它可以包装任何可调用对象,而绑定future则是通过get_future方法获得结果。手动控制执行意味着任务不会自动启动,需要显式调用。 然后,对比其他异步机制。比如std::async的不同,async自动启动任务(取决于策略),而packaged_task需要手动执行。std::promise的区别,promise需要显式设置值,而packaged_task通过调用操作符自动设置。 使用步骤需要具体说明如何创建对象、获取future、执行任务、获取结果。每一步给出示例代码片段,比如创建时模板参数是函数签名,构造时传入函数对象,然后调用get_future,再执行任务,最后用future获取结果。 示例代码部分,可能需要一个完整的例子,展示主线程任务线程的交互,比如将packaged_task移动到线程中执行,并等待结果。这样用户可以看到如何与线程配合使用。 应用场景方面,列举适合使用packaged_task的情况,比如需要将任务传递给线程池、复杂生命周期管理,或者需要多次执行的任务。说明其灵活性控制力强的优势。 注意事项部分,比如移动语义,因为packaged_task不可复制,只能移动。任务执行一次后失效,多次调用会抛出异常。以及异常处理机制,如何将异常传递到future。 最后,总结与std::async的对比,强调packaged_task的灵活性控制能力,适合需要精细管理的场景。 需要确保使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$,但根据之前的例子,用户的问题可能不需要数学公式,更多的是代码术语。所以重点在代码块的正确格式术语的标记,比如用$\texttt{std::packaged_task}$。 需要检查是否有遗漏的关键点,比如promise的对比是否足够清楚,或者使用步骤是否完整。同时,避免技术错误,比如packaged_task的执行是否需要显式调用,是否正确处理异常等。 可能还需要考虑用户的知识水平,是否需要更基础的讲解,但根据之前的问题,用户可能有一定的多线程编程基础,因此可以适当深入,但保持清晰。 现在组织这些内容,确保符合系统级指令的结构要求,分点明确,代码正确,术语准确。</think>$\texttt{std::packaged_task}$ 是 C++11 引入的**任务封装器**,它将可调用对象(函数、Lambda 等)与 $\texttt{std::future}$ 绑定,允许**异步获取任务结果**。以下是其核心特性使用场景: --- ### **1. 核心特性** | 特性 | 描述 | |---------------------|----------------------------------------------------------------------| | **封装任务** | 可包装任何可调用对象(函数、Lambda、成员函数等) | | **绑定结果** | 通过 $\texttt{get_future()}$ 获取关联的 $\texttt{std::future}$ 对象 | | **手动控制执行** | 任务不会自动启动,需显式调用 `operator()` 或传递给线程执行 | --- ### **2. 与其他异步机制对比** | 机制 | 自动启动任务? | 结果传递方式 | 典型用途 | |---------------------|----------------|---------------------------|------------------------------| | $\texttt{std::async}$ | 是(可选策略) | 隐式绑定 $\texttt{future}$ | 快速启动异步任务 | | $\texttt{std::promise}$ | 否 | 显式调用 $\texttt{set_value()}$ | 手动控制结果/跨线程通信 | | $\texttt{std::packaged_task}$ | 否 | 调用任务时自动设置结果 | 灵活的任务-结果绑定 | --- ### **3. 使用步骤** ```cpp #include <iostream> #include <future> #include <thread> int task(int x) { return x * 2; } int main() { // 1. 创建 packaged_task(模板参数为函数签名) std::packaged_task<int(int)> pt(task); // 2. 获取 future 对象 std::future<int> fut = pt.get_future(); // 3. 执行任务(方式一:直接调用) // pt(42); // 在当前线程执行 // 方式二:传递给线程执行 std::thread t(std::move(pt), 42); t.join(); // 4. 获取结果 std::cout << "Result: " << fut.get() << std::endl; // 输出 84 } ``` --- ### **4. 典型应用场景** - **任务队列**:将 $\texttt{packaged_task}$ 对象放入队列,由工作线程取出执行 - **线程池**:作为任务单元传递,配合 $\texttt{future}$ 实现结果回调 - **延迟执行**:先绑定任务与结果通道,稍后选择执行时机 - **复杂任务管理**:需要精确控制任务执行流程的场景 --- ### **5. 关键注意事项** - **移动语义**:$\texttt{packaged_task}$ **不可复制**,只能通过 $\texttt{std::move}$ 转移所有权 ```cpp std::packaged_task<void()> pt1([]{ /*...*/ }); auto pt2 = std::move(pt1); // 正确 // auto pt3 = pt1; // 错误!无法复制 ``` - **一次性执行**:每个 $\texttt{packaged_task}$ 对象**只能执行一次** ```cpp pt(); // 第一次执行正常 pt(); // 抛出 std::future_error 异常 ``` - **异常传递**:任务中抛出的异常会通过 $\texttt{future.get()}$ 重新抛出 ```cpp std::packaged_task<void()> pt([] { throw std::runtime_error("Oops"); }); std::thread t(std::move(pt)); t.join(); try { fut.get(); } catch (const std::exception& e) { std::cout << "Caught: " << e.what() << std::endl; } ``` --- ### **6. 与 std::async 的对比选择** | 场景 | 推荐机制 | 原因 | |---------------------------|-----------------------|-------------------------------| | 快速启动简单异步任务 | $\texttt{std::async}$ | 代码更简洁,自动管理线程 | | 需要精确控制任务执行 | $\texttt{packaged_task}$ | 手动决定执行时间线程上下文 | | 任务需要多次复用 | $\texttt{packaged_task}$ | 可绑定不同参数多次提交执行 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值