C++开发基础——std::future与async异步编程

一,std::future与std::promise

         std::future是一个类模板,存放了线程入口函数的返回结果,调用std::future对象的get()函数可以拿到返回结果。

        std::promise也是一个类模板,可以基于std::promise实现线程之间的数据传输。    

         构造一个std::promise对象时,可以和std::future对象相互关联。

1.std::thread与std::future的对比

        std::thread启动的线程不容易获取线程的计算结果。

        std::thread启动的线程如果抛出了异常,且异常没有被线程本身处理的时候,这个线程会导致整个应用程序发生终止。

        std::future可以很方便地获取线程的执行结果,如果线程抛出了异常,std::future可以将异常转移到另一个线程中,让另一个线程来处理异常。

代码样例: std::future调用get()传递异常给另一个线程

#include <stdio.h>
#include <stdlib.h>
#include <future>
#include <iostream>
#include <stdexcept>//线程函数
int CalculateSum() {throw std::runtime_error("Exception throw from CalculateSum.");
}int main()
{auto future_obj = std::async(std::launch::async, CalculateSum);try {int res = future_obj.get();std::cout << res << std::endl;}catch (const std::exception& ex) {std::cout << "Caught exception: " << ex.what() << std::endl;}
}

运行结果: 

Caught exception: Exception throw from CalculateSum.

2.std::promise和std::future的区别

        同一个线程或者另一个线程将线程函数的计算结果放入到std::promise中,而std::future可以获取std::promise中存储的线程计算结果。因此,std::promise是线程计算结果的输入端,std::future是线程计算结果的输出端。

3.std::future的常用成员函数

        1.get:阻塞式地获得线程返回结果。

        2.wait:等待结果变得可用,此时不会获取线程的执行结果。

        3.wait_for:非阻塞式地获得线程返回结果。

        std::future通过get()获取线程执行结果,如果线程尚未执行结束,对get()的调用将阻塞,直到该结果可以被获取。

        std::future可以先通过调用wait_for()方法,查询结果是否可用来避免阻塞。           

        std::future只能调用一次get()成员函数来获取结果,继续调用多次会引发异常。

4.std::promise的常用成员函数

        1.set_value:指定线程返回结果。

        2.get_future:返回与线程关联的future。

        3.set_exception:指定线程返回的异常。

        std::promise对象只能被移动,不能被复制。

代码样例:子线程和主线程之间同步字符串数据

#include <iostream>
#include <thread>
#include <future>void modifyMessage(std::promise<std::string>&& proms, std::string msg)
{std::string metaMsg = msg + " has been modified";proms.set_value(metaMsg);
}int main()
{std::string msg_str = "My Message";//创建promise对象std::promise<std::string> proms;//创建一个关联的future对象std::future<std::string> future_obj = proms.get_future();//给线程传递promise对象std::thread t(modifyMessage, std::move(proms), msg_str);//打印原始msg_strstd::cout << "Original message from main(): " << msg_str << std::endl;//打印被子线程修改的msg_strstd::string messageFromThread = future_obj.get();std::cout << "Modified message from thread(): " << messageFromThread << std::endl;t.join();return 0;
}

运行结果:

Original message from main(): My Message
Modified message from thread(): My Message has been modified

二,std::shared_future使用说明

        std::shared_future是一个类模板,用法和std::future相似。

        std::shared_future可以让多个线程共享同一个状态,从而实现多线程通信。

std::shared_future的常用成员函数

        1.get:阻塞式地获得线程返回结果。

        2.wait:等待结果变得可用,此时不会获取线程的执行结果。

        3.wait_for:非阻塞式地获得线程返回结果。

        std::shared_future的成员函数的用法和std::future基本一致,主要区别在于,std::shared_future的get()函数是用来复制数据的,而不是移动数据,这样设计可以让多个线程都可以通过get()获取结果。因此,std::future对象只能执行一次get()函数,而std::shared_future对象可以执行多次get()函数。

三,std::async使用说明

        std::async是一个函数模板,通常用来启动一个异步任务,std::async执行结束会返回一个std::future对象。

1.std::async的传参方式

        std::async传参的方式和std::thread十分类似。

        可以使用std::launch给std::async传参,std::launch可以控制是否给std::async创建新线程。

        当不指定std::launch参数时,std::async根据系统资源,自行选择一种执行方法。

结合传参方式,可以总结出,std::async执行线程函数的方法有两种:

        1.创建一个新的线程,异步执行线程函数。

        2.不创建新线程,在主调线程上同步执行线程函数。

通过传参std::launch来让std::async选择指定方式执行线程函数的方法有三种:                                  std::launch::async:创建新线程,异步执行线程函数。

        std::launch::deferred:返回的std::future对象显式调用get()时,在主调线程上同步执行线程函数。

        std::launch::async | std::launch::deferred:代码运行时根据系统资源,选择默认的执行方法。

代码样例:

#include <stdio.h>
#include <stdlib.h>
#include <future>
#include <iostream>//线程函数
int CalculateSum(int a, int b) {std::cout << "In other thread." << std::endl;return a + b;
}int main()
{auto future_obj = std::async(CalculateSum, 12, 16);std::cout << "In Main thread." << std::endl;int res = future_obj.get();std::cout << res << std::endl;
}

运行结果:

In Main thread.In other thread.28

2.std::async和std::thread的区别

        std::thread直接创建线程,而std::async异步创建一个任务,这个任务可以创建新线程,也可以不创建线程,可以避免占用系统资源。

        由于std::async不一定会创建新线程,因此,当系统内存资源不足的时候,继续运行std::thread会使系统崩溃,而std::async此时不会创建新线程,避免了系统崩溃。       std::thread创建的线程不容易获取线程函数的返回值,std::async执行完返回一个std::future对象,可以很容易获取线程函数的返回值。

四,std::packaged_task包装器

        std::packaged_task包装器可以生成一个可调用的对象,并且允许异步获取该对象的执行结果。        

        std::packaged_task是一个类模板,常用的成员函数是get_future(),用于返回一个关联的std::future对象,使用std::packaged_task时可以不需要显式地使用std::promise。

    代码样例:

#include <stdio.h>
#include <stdlib.h>
#include <future>
#include <iostream>//线程函数
int CalculateSum(int a, int b) {return a + b;
}int main()
{std::packaged_task<int(int, int)> task(CalculateSum);auto future_obj = task.get_future();std::thread thread_01{ std::move(task), 12, 16 };int res = future_obj.get();std::cout << res << std::endl;thread_01.join();
}

  运行结果: 

28

  使用std::packaged_task可以将各种可调用对象包装起来,方便作为线程的入口函数来调用。

代码样例: 

 Demo1:packaged_task包装普通函数,然后被调用

#include <stdio.h>
#include <stdlib.h>
#include <future>
#include <iostream>
#include <stdexcept>using namespace std;int mythread(int n)
{cout << "Thread Input: " << n << endl;cout << "mythread start " << "threadId=" << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(2000);std::this_thread::sleep_for(dura);cout << "mythread end " << "threadId=" << std::this_thread::get_id() << endl;return 5;
}int main()
{cout << "Main thread start " << "threadId=" << std::this_thread::get_id() << endl;std::packaged_task <int(int)> mypt(mythread);std::thread t1(std::ref(mypt), 1);t1.join();std::future<int> res = mypt.get_future();cout << "Thread Output: " << res.get() << endl;cout << "Main thread end " << "threadId=" << std::this_thread::get_id() << endl;return 0;
}

运行结果:

Main thread start threadId=4340
Thread Input: 1
mythread start threadId=12168
mythread end threadId=12168
Thread Output: 5
Main thread end threadId=4340

 Demo2:packaged_task包装lambda表达式,然后被调用

#include <stdio.h>
#include <stdlib.h>
#include <future>
#include <iostream>
#include <stdexcept>using namespace std;int main()
{cout << "Main thread start " << "threadId=" << std::this_thread::get_id() << endl;std::packaged_task <int(int)> mypt([](int n){cout << "Thread Input: " << n << endl;cout << "mythread start " << "threadId=" << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(2000);std::this_thread::sleep_for(dura);cout << "mythread end " << "threadId=" << std::this_thread::get_id() << endl;return 10;});std::thread t1(std::ref(mypt), 1);t1.join();std::future<int> res = mypt.get_future();cout << "Thread Output: " << res.get() << endl;cout << "Main thread end " << "threadId=" << std::this_thread::get_id() << endl;return 0;
}

 运行结果:

Main thread start threadId=19596
Thread Input: 1
mythread start threadId=21124
mythread end threadId=21124
Thread Output: 10
Main thread end threadId=19596

Demo3:packaged_task包装成对象,然后被调用

#include <stdio.h>
#include <stdlib.h>
#include <future>
#include <iostream>
#include <stdexcept>using namespace std;int main()
{cout << "Main thread start " << "threadId=" << std::this_thread::get_id() << endl;std::packaged_task <int(int)> mypt([](int n){cout << "Thread Input: " << n << endl;cout << "mythread start " << "threadId=" << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(2000);std::this_thread::sleep_for(dura);cout << "mythread end " << "threadId=" << std::this_thread::get_id() << endl;return 15;});mypt(10); //可调用对象std::future<int> res = mypt.get_future();cout << "Thread Output: " << res.get() << endl;cout << "Main thread end " << "threadId=" << std::this_thread::get_id() << endl;return 0;
}

运行结果:

Main thread start threadId=20868
Thread Input: 10
mythread start threadId=20868
mythread end threadId=20868
Thread Output: 15
Main thread end threadId=20868

 五,参考阅读

《C++新经典》

《C++高级编程》

《深入理解C++11:C++11新特性解析与应用》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/737384.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AcWing 5407. 管道(二分,区间合并)

有一根长度为 l e n len len 的横向的管道&#xff0c;该管道按照单位长度分为 l e n len len 段&#xff0c;每一段的中央有一个可开关的阀门和一个检测水流的传感器。 一开始管道是空的&#xff0c;位于 L i L_i Li​ 的阀门会在 S i S_i Si​ 时刻打开&#xff0c;并不…

下载、安装并配置 Node.js

文章目录 1. 下载2. 自定义安装3. 添加环境变量4. 验证5. 修改下载位置6. npm 换源7. 测试 ➡️➡️➡️来源&#xff1a;Simplilearn.com Node.js 是一个开源、跨平台的 JavaScript 运行时环境和库&#xff0c;用于在客户端浏览器之外运行 web 应用程序。 Ryan Dahl 在2009年开…

React useMemo钩子指南:优化计算性能

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

萝卜大杂烩 | Polars ,最强Pandas平替(内含实操代码,建议收藏!)

本文来源公众号“萝卜大杂烩”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;Polars (最强Pandas平替) 本文章转自&#xff1a;数据studio 1 介绍 Polars是一个用于操作结构化数据的高性能DataFrame库&#xff0c;可以说是平替…

leetcode 热题 100_旋转图像

题解一&#xff1a; 翻转数组&#xff1a;先将数组沿右上-左下对角线翻转&#xff0c;再将数组上下翻转。 class Solution {public void rotate(int[][] matrix) {int n matrix.length;for (int i 0; i < n; i) {//沿右上-左下对角线翻转for (int j 0; j < n - i - 1…

简单句,并列句【语法笔记】

1. 简单句&#xff0c;并列句本质分别是什么 2. 如何区分简单句和并列句 3. 连接词 4. 简单句的五大基本句型 5. 有连接词&#xff0c;未必都是并列句&#xff0c;这是为什么

为何系统对接采用定制开发周期通常比集成平台更长

在信息化建设的浪潮中&#xff0c;系统对接是实现企业内外部系统间数据共享和业务协同的关键环节。在对接开发过程中&#xff0c;定制开发与平台开发是两种主要的实现方式&#xff0c;而普遍观点认为&#xff0c;定制开发对接的周期通常要比平台开发更长。本文将深入探讨这一现…

物奇平台超距断连无蓝牙广播问题解决方法

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, 物奇平台超距断连无蓝牙广播问题解决方法 一 问题反馈 二解决方法: 1 运行流程分析 对应代…

智能合约开发基础知识:最小信任机制、智能合约、EVM

苏泽 大家好 这里是苏泽 一个钟爱区块链技术的后端开发者 本篇专栏 ←持续记录本人自学两年走过无数弯路的智能合约学习笔记和经验总结 如果喜欢拜托三连支持~ 专栏的前面几篇详细了介绍了区块链的核心基础知识 有兴趣学习的小伙伴可以看看http://t.csdnimg.cn/fCD5E关于区块…

Effective C++ 学习笔记 条款24 若所有参数皆需类型转换,请为此采用non-member函数

作者在导读中提过&#xff0c;令class支持隐式类型转换通常是个糟糕的主意。当然这条规则有其例外&#xff0c;最常见的例外是在建立数值类型时。假设你设计一个class用来表现有理数&#xff0c;允许整数“隐式转换”为有理数似乎颇为合理。的确&#xff0c;它并不比C内置从int…

线上又出问题了!又是特殊场景,哎呀,当时怎么没有想到!

目录标题 导读01.为什么经常会发生测试场景覆盖不全的问题?02.如何提升测试覆盖度?03.综述 导读 在我们的测试工作中&#xff0c;是不是经常遇到这样的情形&#xff0c;发生了线上问题&#xff0c;产品、研发或者测试同学一拍脑袋&#xff1a;当时怎么没有想到&#xff0c;怎…

Vue.js+SpringBoot开发海南旅游景点推荐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

【论文阅读】VMamba:视觉状态空间模型

文章目录 VMamba:视觉状态空间模型摘要相关工作状态空间模型 方法准备状态空间模型离散化选择扫描机制 2D 选择扫描VMamba 模型整体结构VSS块 实验分析实验有效感受野输入尺度 总结 VMamba:视觉状态空间模型 摘要 受最近提出的状态空间模型启发&#xff0c;我们提出了视觉状态…

一款适合程序员开发复杂系统的通用平台——JNPF 开发平台

在过去&#xff0c;很多开发工具更侧重代码编辑&#xff0c;针对数据库增删改查&#xff08;CRUD&#xff09;类的 Web 系统开发&#xff0c;在界面设计、前后端数据交互等环节主要还是靠写代码&#xff0c;效率比较低。目前很多所谓的低代码开发平台&#xff0c;大多数也都是基…

Linux第75步_pinctrl子系统驱动和gpio子系统的常用函数

1、STM32MP1的pinctrl子系统驱动 pinctrl子系统源码目录为drivers/pinctrl&#xff0c;一个PIN最好只能被一个外设使用。 “stm32mp151.dtsi”中有一个“pin-controller节点标签”叫pinctrl pinctrl: pin-controller50002000 { #address-cells <1>; /*定义子节点的…

【24春招/Java】Java的市场情况介绍及Spring Boot基础

Java的市场情况 需求减少 人员增加 面试难度增加 1、Java高级开发&#xff0c;100份简历&#xff0c;筛选了20份&#xff0c;邀约20个人面试 面试机会&#xff1a;录取人数20:1 充分准备通过面试的前提 不要裸面&#xff01;&#xff01;&#xff01; 背葵花宝典&#xff08;…

23.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-实现配置工具数据结构

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;22.加载配置文件…

站库分离技术--反向代理技术-雷池云WAF-给自己搭建一个安全点的网站

文章目录 概要整体架构流程技术名词解释技术细节ssh-ubuntu服务器docker-映射-链接-通信nginx反代mysql设置数据库新密码 小结我的mysql映射目录我的wordpress映射目录 成果展示 概要 新买了一个云服务器&#xff0c;想搭建一个站库分离的wordpress为主的网站&#xff0c;采用d…

PID控制器组(完整SCL代码)

PID控制器组不是什么新概念,是在PID控制器的基础上,利用面向对象的思想对对象进行封装 批量实例化。 1、增量式PID https://rxxw-control.blog.csdn.net/article/details/124363197https://rxxw-control.blog.csdn.net/article/details/1243631972、完全增量式PID https:/…

Vue 3:引领前端开发的未来

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…