C++11之future,promise,packaged_task,async详解

C++11异步编程实战:std::async、packaged_task与promise/future详解 异步编程是现代软件开发中提升程序响应性和资源利用率的核心技术,其核心原理在于将耗时任务从主执行流中分离,允许程序在等待操作完成时继续执行其他工作。在C++中,C++11标准引入的<future>库为此提供了语言级别的支持,通过std::future对象作为异步操作结果的占位符,实现了任务与结果获取的解耦。这项技术的核心价值在于简化并发逻辑、充分利用多核CPU资源,并有效避免主线程阻塞,从而显著提升网络服务、文件I/O及图形界面等场景下的应用性能。本文聚焦于C++11异步编程的三种核心工具:std::asyn 阅读详情

C++11为了给我们提供更便利的并发编程,加入了 future,promise,packaged_task,async 这几个新特性,那么它们到底有什么作用又能给我们带来哪些便利呢?


经典的生产-消费模型

mutex + condition_variable:

首先让我们来看一个最经典的生产-消费模型:

[cpp]  view plain  copy
  1. #include <thread>  
  2. #include <mutex>  
  3. #include <iostream>  
  4. #include <chrono>  
  5. #include <memory>  
  6. #include <condition_variable>  
  7.   
  8. struct _data  
  9. {  
  10.     bool ready;  
  11.     int32_t value;  
  12. };  
  13.   
  14. _data data = { false, 0 };  
  15. std::mutex data_mutex;  
  16. std::condition_variable data_con;  
  17.   
  18. int main()  
  19. {  
  20.     //生产数据的线程  
  21.     std::thread prepare_data_thread([](){  
  22.         std::this_thread::sleep_for(std::chrono::seconds(2));    //模拟生产过程,   
  23.   
  24.         std::unique_lock<std::mutex> ulock(data_mutex);  
  25.         data.ready = true;  
  26.         data.value = 1;  
  27.   
  28.         data_con.notify_one();  
  29.     });  
  30.   
  31.     //消费数据的线程  
  32.     std::thread process_data_thread([](){  
  33.         std::unique_lock<std::mutex> ulock(data_mutex);  
  34.         cv.wait(ulock, [](){ return data.ready; });  
  35.           
  36.         std::cout << data.value << std::endl;  
  37.     });  
  38.   
  39.     prepare_data_thread.join();  
  40.     process_data_thread.join();  
  41.   
  42.     system("pause");  
  43.     return 0;  
  44. }  


为了不浪费系统资源,我们使用条件变量来使 process_thread 即消费线程进入休眠等待,直到数据准备好了然后通知它来处理。这里为了在线程间进行数据传递,我们需要一个全局变量 data,然后利用锁和条件变量这些机制来保证共享数据的安全。有没有更简便的方法来实现线程间的通信及同步呢?答案当然是 of course,下面我将使用C++11的这些新特性来实现这些需求。


在实现之先,还是有必要先简单讲一讲 future,promise,packaged_task,async 之间的关系:

从字面意思来看,future 翻译成期望,期望得到某一个结果,当然想要获取这个结果肯定是需要一定等待的,而产出这个结果的就可以是一个异步操作了。那么这个结果从哪里获取呢?这个时候就不得不用到 promise,packaged_task,async 这几个东西了。

promise:

顾名思义,承诺,承诺给别人反馈一个结果,那么 promise 必定就是和 future 是好基友了,通过 promise 反馈一个结果,然后再通过 future 来获取这个结果。具体怎么来使用呢?让我们还是用上面的生产-消费者模型来解释:

[cpp]  view plain  copy
  1. #include <thread>  
  2. #include <iostream>  
  3. #include <future>  
  4. #include <chrono>  
  5.   
  6. struct _data  
  7. {  
  8.     int32_t value;  
  9. };  
  10.   
  11. _data data = { 0 };  
  12.   
  13. int main()  
  14. {  
  15.     std::promise<_data> data_promise;      //创建一个承诺  
  16.     std::future<_data> data_future = data_promise.get_future();     //得到这个承诺封装好的期望  
  17.   
  18.     std::thread prepare_data_thread([](std::promise<_data> &data_promise){  
  19.         std::this_thread::sleep_for(std::chrono::seconds(2));    //模拟生产过程  
  20.   
  21.         data_promise.set_value({ 1 });       //通过set_value()反馈结果  
  22.     }, std::ref(data_promise));  
  23.   
  24.     std::thread process_data_thread([](std::future<_data> &data_future){  
  25.         std::cout << data_future.get().value << std::endl;    //通过get()获取结果  
  26.     }, std::ref(data_future));  
  27.   
  28.     prepare_data_thread.join();  
  29.     process_data_thread.join();  
  30.   
  31.     system("pause");  
  32.     return 0;  
  33. }  


promise 是一个类模板,模板参数便是产出值的类型。同样的,future 也是一个类模板,模板参数是获取的值的类型。

我们首先创建了一个 _data 类型data_promise ,而在 data_promise 里已经封装好了一个 _data类型的future,我们可以调用 promise 的 get_future() 方法得到与之对应的 future。然后我们把 data_promise 传递给了 prepare_data_thread,于是我们便可以在 prepare_data_thread 里面来产出值了,在休眠2S之后,我们调用了 set_value() 方法来产出值。我们又将和 data_promise 相关联的 data_future 传递给了 process_data_thread,所以我们便可以在 process_data_thread 里调用 data_future 的 get() 方法获取 data_promise 的产出值。这里需要注意的一点是,future 的 get() 方法是阻塞的,所以在与其成对的 promise 还未产出值,也就是未调用 set_value() 方法之前,调用 get() 的线程将会一直阻塞在 get()处直到其他任何人调用了 set_value() 方法。


简单来说,promise 用来产出值,future 用来获取值,但这个 promise 和 future 必须是配对的,也就是说 future 是通过调用 promise 的 get_future() 方法来获得的。


packaged_task:

packaged_task 是对一个任务的抽象,我们可以给其传递一个函数来完成其构造。相较于 promise,它应该算是更高层次的一个抽象了吧,同样地,我们可以将任务投递给任何线程去完成,然后通过 packaged_task.get_future() 方法获取的 future 来获取任务完成后的产出值。那么,下面还是原来的配方:

[cpp]  view plain  copy
  1. #include <thread>  
  2. #include <iostream>  
  3. #include <future>  
  4.   
  5. struct _data  
  6. {  
  7.     int32_t value;  
  8. };  
  9.   
  10. _data data = { 0 };  
  11.   
  12. int main()  
  13. {  
  14.     std::packaged_task<_data()> prepare_data_task([]()->_data{  
  15.         std::this_thread::sleep_for(std::chrono::seconds(2));    //模拟数据生产过程  
  16.   
  17.         return{ 1 };  
  18.     });  
  19.     auto data_future = prepare_data_task.get_future();          //获取future  
  20.   
  21.     std::thread do_task_thread([](std::packaged_task<_data()> &task){  
  22.         task();              //调用packaged_task的调用符来运行任务  
  23.     }, std::ref(prepare_data_task));  
  24.   
  25.     std::thread process_data_thread([](std::future<_data> &data_future){  
  26.         std::cout << data_future.get().value << std::endl;  
  27.     }, std::ref(data_future));  
  28.   
  29.     do_task_thread.join();  
  30.     process_data_thread.join();  
  31.   
  32.     system("pause");  
  33.     return 0;  
  34. }  


packaged_task 也是一个类模板,模板参数为函数签名,也就是传递函数的类型,如上例中为 _data(),返回值为 _data 类型,函数参数为 void,其中返回值类型将决定 future 的类型也就是产出值类型。

我们创建了一个任务,并投递给了 do_task_thread 去完成这个任务,然后将对应的 data_future投递 给了process_data_thread ,于是我们就可以在 process_data_thread 里获取任务产出值了。同样地,获取值之前必须等待任务的完成。


到了这里,还差一个 async 没有讲,可能你会有一肚子问题,说到底这几个东西不就是可以在线程之间实现通信及同步吗?是的,它为我们省去了使用 mutex,condition_variable 这些枯燥的细节,也降低了我们出错的可能,但是绝对不要以为 future 是线程安全的,future.get()只能被调用一次,多次调用会触发异常,如果想要在多个线程中多次获取产出值请查阅 shared_future。当然,智者见智仁者见仁,用不用是你的选择。


async:

最后,来看看 async 怎么使用。

当我想要异步完成一个操作,并在主线程中阻塞地或者非阻塞地获取这个异步操作的结果时,通常我们的做法是 new 一个子线程来完成这个异步操作,并结合锁,条件变量在主线程中来获取共享内存中的值。async 为我们实现了这些细节,让我们无需再亲自去开辟线程,并且提供了更多可选的功能。为了与之前的内容形成对比,我们仍然采用老套的生产-消费模型来举例:

[cpp]  view plain  copy
  1. #include <thread>  
  2. #include <iostream>  
  3. #include <chrono>  
  4. #include <future>  
  5.   
  6. struct _data  
  7. {  
  8.     int32_t value;  
  9. };  
  10.   
  11. _data data = { 0 };  
  12.   
  13. int main()  
  14. {  
  15.     auto start = std::chrono::steady_clock::now();  
  16.   
  17.     std::future<_data> data_future = std::async(std::launch::async, []()->_data{  
  18.         std::this_thread::sleep_for(std::chrono::seconds(1));           //模拟生产过程  
  19.         return { 1 };  
  20.     });  
  21.   
  22.     std::this_thread::sleep_for(std::chrono::seconds(2));  
  23.     std::cout << data_future.get().value << std::endl;              //使用产出值  
  24.   
  25.     auto end = std::chrono::steady_clock::now();  
  26.     std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << std::endl;  
  27.   
  28.     system("pause");  
  29.     return 0;  
  30. }  

async 返回一个与函数返回值相对应类型的 future,通过它我们可以在其他任何地方获取异步结果。

由于我们给 async 提供了 std::launch::async 策略,所以生产过程将被异步执行,具体执行的时间取决于各种因素,最终输出的时间为 2000ms+,可见生产过程和主线程是并发执行的。

除了 std::launch::async,还有一个 std::launch::deferred 策略,它会延迟线程地创造,也就是说只有当我们调用 future.get() 时子线程才会被创建以执行任务。你可以自己改一下看看最终的输出结果是不是 3000ms+。

别再傻等线程结果了!C++11 std::future 三种用法实战(附async/packaged_task/promise对比) 本文深入解析C++11中std::future的三种用法,包括std::async、std::packaged_task和std::promise,帮助开发者高效处理多线程任务结果获取问题。通过实战代码和对比分析,展示如何在不同场景下选择最佳方案,提升代码性能和可维护性。 阅读详情

相关推荐

C++异步编程实战:用std::async提升程序响应与性能

异步编程是现代软件开发中提升程序响应性和性能的核心技术。其基本原理是将耗时操作从主执行流程中分离,允许程序在等待任务完成时继续处理其他工作,从而避免阻塞。这种模式在多核CPU环境下尤其重要,能够充分利用硬件并行能力,显著提升吞吐量。在C++中,标准库提供了<future>头文件,封装了高级异步操作抽象,让开发者无需直接管理线程细节。通过std::async、std::future和std::promise等工具,可以轻松实现任务的并发执行与结果同步。这些技术广泛应用于网络服务、图形界面、数据处理等需要高并发

weixin_30443747的博客 357

c++11多线程编程同步——使用future和promise

简介 在多线程编程中,需要数据共享,如一个线程需要使用另一线程运算产生的数据。 涉及到异步编程时,有时需要线程间同步,如执行异步订阅消息时,当订阅消息的结果返回成功后,处理消息的线程才需要运行。 以上场景的都可以使用std::future和std::promise实现。 std::promise是可以存储类型T的值的对象,该值可以被另一线程的std::future对象获取,并提供了同步机制。 ...

guotianqing的博客 5725

C++异步编程:深入理解std::future机制与实战应用

异步编程是现代软件开发中提升性能与响应能力的关键技术,其核心思想是将耗时操作从主线程分离,实现任务的并行执行。在C++中,这一机制通过标准库提供的并发组件实现,其中std::future作为异步操作结果的承载者,扮演着核心角色。它采用生产者-消费者模式,允许程序在发起任务后继续执行其他逻辑,待任务完成后再获取结果,从而有效避免了同步阻塞导致的资源浪费。从技术价值看,异步编程能显著提升多核CPU利用率,适用于高并发服务器、实时数据处理等计算密集型场景。本文聚焦于C++11引入的std::future及其相关工

weixin_34025151的博客 358

C++线程/线程池:thread、mutex、condition_variable、future、promise

C++线程:thread、mutex、condition_variable、future、promise

u013250861的博客 352

C++11异步操作(二)

1、async async是一个函数模板,用来启动一个异步任务,启动异步任务后,返回一个future对象。这个future对象里含有线程入口函数锁返回的结果(线程返回结果),可通过future对象的成员函数get()来获取。 2、future (1)future: 将来的意思,提供了一种访问异步操作结果的机制,此结果可能没马上拿到,可在线程执行完毕后拿到...

king_weng的博客 810

C++并发实战13:std::future、std::async、std::promise、std::packaged_task

std::condition_variable可以用于异步事件的重复通知,但是有些时候可能只等待事件发生一次,比如:等待特定的航班,用条件变量大杀器有点浪费了。C++11标准库提供了几种异步任务机制。通常thread不能返回线程执行的结果(可以通过引用参数返回),而在异步处理当中很多时候都需要获得计算的结果。如果只获取结果一次那么选用future,即通过future获取了结果后,后续再通过此fut

liuxuejiang158的专栏 1万+

c++11 condition_variable && future

条件变量std::condition_variable用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程。std::condition_variable需要与std::unique_lock配合使用  #include &lt;condition_variable&gt; #include &lt;iostream&gt; #include &lt;mutex&gt; #include &...

qq_33795447的博客 629

C++11 多线程 future/promise简介

1. < future >头文件简介 Classes std::future std::future_error std::packaged_task std::promise std::shared_future Functions std::async std::future_category 2. std::future简单来说,std::future提供了一种访问异步操作结果的

jiange_zh的博客 3万+

C++11 异步编程核心组件详解:从 std::async ,std::future,std::promise到 std::packaged_task

std::async 是 C++11 提供的异步任务处理工具,通过 std::future 获取结果,简化了多线程编程。它支持三种启动策略:立即异步执行(async)、延迟同步执行(deferred)和默认自动选择。std::future 作为异步编程核心组件,用于获取任务结果或状态,与 std::async 配合实现异步任务的结果传递。两者共同构成了 C++ 异步编程模型的基础,适用于单次、需要返回结果的异步场景,但高频任务建议使用线程池以提高效率。

2301_77509762的博客 1344

C++ 并发编程:std::promise/future、std::async、std:: packaged_task

1、std::promise 和 std::future future 和 promise 的作用是在不同线程之间传递数据,而且支持泛型的操作,使用起来更加方便。 使用 future 和 promise 在线程之间获取数据的机制又是什么呢? 假设线程 1 需要线程 2 的数据,则: 线程 1 初始化一个 promise 对象和一个 future 对象,promise 传递给线程2,相当于线程 2 对线程 1 的一个承诺;future 相当于一个接受一个承诺,用来获取未来线程 2 传递的值; 线程 2 获取

深入浅出讲透复杂深奥的问题 1851

H无穷控制学习笔记——加权灵敏度H无穷控制问题

一、加权灵敏度H无穷问题 系统结构如上图所示。被控对象P的状态空间方程为(Ap,Bp,Cp,DpA_p,B_p,C_p,D_pAp​,Bp​,Cp​,Dp​),加权函数W1的状态空间方程为(A1,B1,C1,D1A_1,B_1,C_1,D_1A1​,B1​,C1​,D1​),加权函数W2的状态空间方程为(A2,B2,C2,D2A_2,B_2,C_2,D_2A2​,B2​,C2​,D2​),加权函数W3的状态空间方程为(A3,B3,C3,D3A_3,B_3,C_3,D_3A3​,B3​,C3​,D3​)。

一饮丶一啄的博客 4346

C++并发编程:深入解析std::promise核心机制与实战应用

在多线程编程中,线程间通信与数据同步是核心挑战。传统方法如互斥锁与条件变量不仅代码复杂,还易引发死锁与虚假唤醒等问题。C++11引入的future/promise模型,通过共享状态与同步语义,为异步操作提供了安全高效的结果传递机制。其原理在于将数据的生产与消费解耦,生产者通过promise设置值或异常,消费者通过future等待并获取结果,底层内存模型保证了操作的可见性与顺序性。这一机制的技术价值在于显著简化了并发代码的编写,提升了程序的可靠性与可维护性。在工程实践中,该模型广泛应用于线程结果返回、事件通知

lvwenwen 554

CNN经典论文(一)LeNet

Gradient-Based Learning Applied to Document Recognition(梯度学习在文档识别中的应用) Yann LeCun, Leon Bottou, Yoshua Bengio, and Patrick Haffner 发表时间:December 1998 期刊:Proceedings of the IEEE 影响:世界上第一个卷积神经网络 论文地址:Gradient-Based Learning Applied to Document Recognition

loveling丶的博客 3597

C++11——异步

异步是把任务提交之后,主线程无需等待继续向下执行,当任务执行完之后(可通过线程、协程等方式),将结果存入future,主线程通过future来获得结果。future是异步编程中承载异步结果的核心对象。这里的结果是个宽泛的概念:在程序正常结束时是返回值,在程序出现错误时是异常信息。

2301_80662872的博客 348

【模拟电子设计实战】开源!基于Multisim与AD620的心电采集电路仿真与优化

本文详细介绍了基于Multisim与AD620的心电采集电路仿真与优化实战。通过分析心电信号采集的挑战,重点讲解了AD620仪表放大器和OP07运算放大器的选型优势及电路设计技巧,包括对抗50Hz工频干扰和基线漂移的优化方法。文章还分享了Multisim仿真参数设置与实测对比经验,最终实现性能达标的心电采集电路设计。

weixin_30555125的博客 95

Clion编译stm32cube初始化工程时non constant or forward reference address expression for section .ARM.extab报错

相信应该有同学在看了稚晖君关于clion开发stm32的文章之后也想在自己PC上实现“优雅の嵌入式开发”遇到和我类似的问题。他的文章是22年写的,我个人猜测应当是版本更新导致的不适配。意思就是说 READONLY 只能在gcc11以后的版本中使用,gcc10及以前的版本将无法正确识别,也就有如下错误。或者就可以将gcc升级至最新的版本。记得在clion工具链中修改c,cpp编译器的位置。可以将文本括号中的 READONLY 全部删除。当时由于没有注意到这行注释导致在这里卡了好久。

2301_77256849的博客 2508

【C++】五分钟带你了解C++系列(六):异步处理

我们日常写的程序大部分的都是基于同步( Synchronous),即按编码顺序一条条执行——这是最符合人类直觉的编码方式,然而当I/O的速度和CPU计算的速度不匹配时,会出现CPU资源浪费,为了解决这种问题,C++提供了异步(Asynchronous)处理的方法。

GentalYuan的博客 317

告别密码本!用Windows NPS给华为/华三交换机做Radius认证,SSH登录更安全

本文详细介绍了如何利用Windows NPS实现华为/华三交换机的Radius认证,提升SSH登录安全性。通过集中化管理、精细化控制和完整审计追踪,企业可告别传统密码本,大幅提升网络设备管理效率。文章包含NPS服务部署指南、交换机配置详解及调试技巧,助力企业构建安全高效的统一认证体系。

weixin_42549723的博客 418

C++11并发与多线程笔记(9) async、future、packaged_task、promise

第九节、async、future、packaged_task、promise 本节内容需要包含头文件#include 一、std::async、std::future创建后台任务并返回值 std::async是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future对象,这个对象是个类模板。 什么叫“启动一个异步任务”?就是自动创建一个线程,并开始 执行对应的线程入口函数,它返回一个std::future对象,这个std::future对象中就含有线程入口函数所返回

qq_38231713的博客 4441

2026 最新安全 Skill 合集(CTF / 审计 / 渗透 / 红队)威胁建模一网打尽

2026年安全领域进入"Skill化"时代,各类安全能力被结构化封装为可复用的AI Skill。本文介绍了7个核心安全Skill项目:渗透测试全流程的AgentSkillsHub、CTF解题技能库ctfskills、多语言代码审计工具codeaudit、PHP专项审计系统、Java自动化审计流水线、威胁建模工具threatmodeling以及覆盖30+领域的Anthropic-Cybersecurity-Skills。这些项目将传统安全经验转化为标准化、可验证、可自动执行的模块化能力,支

Chengbei11的博客 1855

20、认知负荷理论在教育技术中的应用

本文深入探讨了认知负荷理论(Cognitive Load Theory, CLT)在教育技术中的应用,重点分析了如何通过优化教学设计减少学习者的认知负荷,提高学习效率。文章介绍了认知负荷的三种类型:内在认知负荷、外在认知负荷和germane认知负荷,并结合多媒体学习原则(如模态效应、空间连续性原则、时间连续性原则等)提出了减少外在认知负荷的具体策略。此外,文章通过实证研究和教学案例,展示了认知负荷理论在不同学科和个性化教学中的广泛应用和有效性。最终,文章总结了应用认知负荷理论进行教学设计的具体流程,强调其在

solidity8miner的博客 922

C++11异步操作future和aysnc 、function和bind

本文介绍异步操作future和aysnc 与 function和bind

清风 2193

华为OD机试 - 报文响应时间 - 贪心算法、位运算(Python/JS/C/C++ 2024 E卷 200分)

贪心算法是在每一步选择中都做出当前最优的选择,期望通过局部最优解推导出全局最优解的算法。

学Java,找哪吒 1757

C++11异步编程(std::async, std::future, std::packaged_task, std::promise)

std::async是一个函数模板,会启动一个异步任务,最终返回一个std::future对象。在之前我们都是通过thread去创建一个子线程,但是如果我们要得到这个子线程所返回的结果,那么可能就需要用全局变量或者引用的方法来得到结果,这样或多或少都会不太方便,那么async这个函数就可以将得到的结果保存在future中,然后通过future来获取想要得到的结果。async比起...

Charles_Zaqdt的博客 1082

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

多线程编程6. std::future、std::promise、std::packaged_task和std::async

std::future提供了一种访问异步操作结果的机制。 如果我们想要得到一个线程运行的结果,一般来说,需要用到全局变量,但是从安全的角度上来说,全局变量并不合适。因此,C++11提供了std::future类模板。

zhimin_zeng的博客 1034

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

上一篇: 浅析 C++ 之 RAII
下一篇: 深入解析C++ STL中的常用容器
imJaron
博客等级 码龄11年 121粉丝 286原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值