C++11并发与多线程笔记(9) async、future、packaged_task、promise
C++11并发与多线程笔记(9) async、future、packaged_task、promise
- 1、std::async、std::future创建后台任务并返回值
- 2、std::packaged_task:打包任务,把任务包装起来
- 3、std::promise
- 3、小结
1、std::async、std::future创建后台任务并返回值
std::async: 是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future对象,这个对象是个类模板。
什么叫“启动一个异步任务”?就是自动创建一个线程,并开始执行对应的线程入口函数,它返回一个std::future对象,这个std::future对象中就含有线程入口函数所返回的结果,我们可以通过调用future对象的成员函数get()来获取结果。
“future”将来的意思,也有人称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没办法马上拿到,但是在不久的将来,这个线程执行完毕的时候,你就能够拿到结果了,所以,大家这么理解:future中保存着一个值,这个值是在将来的某个时刻能够拿到。
std::future对象的get()成员函数会等待线程执行结束并返回结果,拿不到结果它就会一直等待,感觉有点像join()。但是,它是可以获取结果的。
std::future对象的wait()成员函数,用于等待线程返回,本身并不返回结果,这个效果和 std::thread 的join()更像。
#include <iostream>
#include <future>
using namespace std;
class A {
public:int mythread(int mypar) {cout << mypar << endl;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);//定义一个5秒的时间std::this_thread::sleep_for(dura);//创建一个线程并开始执行,绑定关系cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;//卡在这里等待mythread()执行完毕,拿到结果return mypar;}
};int mythread() {cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);//定义一个5秒的时间std::this_thread::sleep_for(dura);//创建一个线程并开始执行,绑定关系cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;//卡在这里等待mythread()执行完毕,拿到结果return 5;
}int main() {A a;int tmep = 12;cout << "main" << "threadid = " << std::this_thread::get_id() << endl;std::future<int> result1 = std::async(mythread);cout << "continue........" << endl;cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果,只能使用一次//类成员函数std::future<int> result2 = std::async(&A::mythread, &a, temp); //第二个参数是对象引用才能保证线程里执行的是同一个对象cout << result2.get() << endl;//或者result2.wait(); //等待线程返回,本身不返回结果cout << "good luck" << endl;return 0;
}
我们通过向std::async()传递一个参数,该参数是std::launch类型(枚举类型),来达到一些特殊的目的:
- std::lunch::deferred:
(defer推迟,延期)表示线程入口函数的调用会被延迟,一直到std::future的wait()或者get()函数被调用时(由主线程调用)才会执行;如果wait()或者get()没有被调用,则不会执行。
重点:实际上根本就没有创建新线程。std::launch::deferred意思时延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。
#include <iostream>
#include <future>
using namespace std;
class A {
public:int mythread(int mypar) {cout << mypar << endl;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);std::this_thread::sleep_for(dura);cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;return mypar;}
};int main() {A a;int temp = 12;cout << "main" << "threadid = " << std::this_thread::get_id() << endl;cout << "continue........" << endl;std::future<int> result1 = std::async(std::launch::deferred, &A::mythread, &a, temp);cout << result1.get() << endl;//或者result2.wait();cout << "I love China!" << endl;return 0;
}

- std::launch::async,在调用async函数的时候就开始创建新线程,不添加标记,默认用的就是默认值是 std::launch::async | std::launch::deferred 标记。
int main() {A a;int temp = 12;cout << "main" << "threadid = " << std::this_thread::get_id() << endl;cout << "continue........" << endl;std::future<int> result1 = std::async(std::launch::async, &A::mythread, &a, temp);//使用std::launch::async标记cout << result1.get() << endl;//或者result2.wait();cout << "I love China!" << endl;return 0;
}
- 同时使用std::launch::async和std::lunch::deferred标记,并不能在新线程中延迟调用。
2、std::packaged_task:打包任务,把任务包装起来
类模板,它的模板参数是各种可调用对象,通过packaged_task把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
#include <thread>
#include <iostream>
#include <future>
using namespace std;int mythread(int mypar) {cout << mypar << endl;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);std::this_thread::sleep_for(dura);cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;return 5;
}int main() {cout << "main" << "threadid = " << std::this_thread::get_id() << endl;//我们把函数mythread通过packaged_task包装起来//参数是一个int,返回值类型是int//方法1std::packaged_task<int(int)> mypt(mythread);std::thread t1(std::ref(mypt), 1);//线程开始执行t1.join();//等待线程执行完毕std::future<int> result = mypt.get_future(); //std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。cout << result.get() << endl;return 0;
}
可调用对象可由函数换成lambda表达式
int main() {//方法2,用lambda表达式std::packaged_task<int(int)> mypt([](int mypar){cout << mypar << endl;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);std::this_thread::sleep_for(dura);cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;return 5;});std::thread t1(std::ref(mypt), 1);t1.join();std::future<int> result = mypt.get_future(); //std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。cout << result.get() << endl;cout << "I love China!" << endl;return 0;
}
packaged_task包装起来的可调用对象还可以直接调用,从这个角度来讲,packaged_task对象也是一个可调用对象
lambda的直接调用
int main() {//方法2,用lambda表达式std::packaged_task<int(int)> mypt([](int mypar){cout << mypar << endl;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);std::this_thread::sleep_for(dura);cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;return 5;});//packaged_task包装起来的可调用对象还可以直接调用,所以从这个角度来讲,pakcaged_task对象,也是一个可调用对象;mypt(105);//直接调用,相当于函数调用std::future<int> result=mypt.get_future();cout<<result.get()<<endl; return 0;
}
包装后存放容器里
vector<std::packaged<int(int)>> mytasks;
int main() {//方法2,用lambda表达式std::packaged_task<int(int)> mypt([](int mypar){cout << mypar << endl;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);std::this_thread::sleep_for(dura);cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;return 5;});mytasks.push_back(std::move(mypt));//入容器,进去用了移动语义,入进去之后mypt就为空std::packaged_task<int(int)>mypt2;auto iter=mytask.begin();mypt2=std::move(*iter);//移动语义mytasks.erase(iter);//删除第一个元素,迭代已经失效了,所以后续代码不可以再使用itermypt2(105);//直接调用,相当于函数调用std::future<int> result=mypt2.get_future();cout<<result.get()<<endl; return 0;
}
3、std::promise
类模板,我们能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值取出来
#include <thread>
#include <iostream>
#include <future>
using namespace std;void mythread(std::promise<int> &tmpp, int clac) {//做一系列复杂的操作clac++;clac *=10;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);std::this_thread::sleep_for(dura);cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;int result = clac;//保存结果tmp.set_value(result); //结果保存到了tmp这个对象中return;
}int main() {std::promise<int> myprom;std::thread t1(mythread, std::ref(myprom), 180);t1.join(); //在这里线程已经执行完了std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值auto result = fu1.get();//get只能调用1次cout << "result = " << result << endl;cout<<"I love China!"<<endl;
}
总结:通过promise保存一个值,在将来某个时刻我们通过把一个future绑定到这个promise上,来得到绑定的值
使用两个子进程
#include <thread>
#include <iostream>
#include <future>
using namespace std;void mythread(std::promise<int> &tmpp, int clac) {//做一系列复杂的操作clac++;clac *=10;cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;std::chrono::milliseconds dura(5000);std::this_thread::sleep_for(dura);cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;int result = clac;//保存结果tmp.set_value(result); //结果保存到了tmp这个对象中return;
}void mythread2(std::future<int> &tmpf){auto result=tmpf.get();cout<<"mythread2.result"<<result<<endl;return;
}int main() {std::promise<int> myprom;std::thread t1(mythread, std::ref(myprom), 180);t1.join(); //在这里线程已经执行完了std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值std::thread t2(mythread2,std::ref(ful));t2.join();//等mythread2线程执行完毕cout<<"I love China!"<<endl;
}
总结:第一个线程1(t1) 计算了一个结果,结果通过future对象给到第二个线程2(t2)。
注意:使用thread时,必须 join() 或者 detach() 否则程序会报异常
3、小结
我们学习这些东西的目的并不是,要把他们都用到实际开发中。
相反,如果我们能够用最少的东西写出一个稳定的,高效的多线程程序,更值得赞赏。
我们为了成长必须阅读一些高手写的代码,从而实现自己代码的积累;
相关文章:
C++11并发与多线程笔记(9) async、future、packaged_task、promise
C11并发与多线程笔记(9) async、future、packaged_task、promise 1、std::async、std::future创建后台任务并返回值2、std::packaged_task:打包任务,把任务包装起来3、std::promise3、小结 1、std::async、std::future创建后台任务…...
Mr. Cappuccino的第63杯咖啡——Spring之AnnotationConfigApplicationContext源码分析
Spring之AnnotationConfigApplicationContext源码分析 源码分析 源码分析 以上一篇文章《Spring之Bean的生命周期》的代码进行源码分析 AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(SpringConfig02.class); LifeCycleBe…...
opencv直方图与模板匹配
import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows() 直方图 cv2.calcHist(images,channels,mask,histSize,ran…...
Apache Doris 入门教程31:计算节点
需求场景 目前Doris是一个典型Share-Nothing的架构, 通过绑定数据和计算资源在同一个节点获得非常好的性能表现. 但随着Doris计算引擎性能持续提高, 越来越多的用户也开始选择使用Doris直接查询数据湖数据. 这类场景是一种Share-Disk场景, 数据往往存储在远端的HDFS/S3上, 计…...
Nacos和GateWay路由转发NotFoundException: 503 SERVICE_UNAVAILABLE “Unable to find
问题再现: 2023-08-15 16:51:16,151 DEBUG [reactor-http-nio-2][CompositeLog.java:147] - [dc73b32c-1] Encoding [{timestampTue Aug 15 16:51:16 CST 2023, path/content/course/list, status503, errorService Unavai (truncated)...] 2023-08-15 16:51:16,17…...
2021年9月全国计算机等级考试真题(二级C语言)
2021年9月全国计算机等级考试真题(二级C语言) 第1题 下列叙述中正确的是( )。 A. 算法的复杂度是指算法所处理的数据量 B. 算法的复杂度是指算法程序中指令的数量 C. 算法的复杂度是指算法控制结构的复杂程度 D. 算法的复杂度包…...
串口通讯
USART是全双工同步通讯 在同步通信中,数据信号所传输的内容绝大多数属于有效数据,而异步通信中包含了各种帧的标识符,所以同步通讯的效率更高。但是同步通信对时钟要求苛刻,允许的误差小。而异步通信则允许双方的误差较大 比特率…...
自动拉取 GitHub 仓库更新的脚本
更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 由于将 HAUE-CS-WIKI 部署到了我自己的服务器上作为国内镜像站,每次在源站更新后都需要手动拉取镜像站的更新实在是太麻烦了,因此产生了编写该脚本的需求( 读者可根据该…...
如何获得Android 14复活节彩蛋
每个新的安卓版本都有隐藏复活节彩蛋的悠久传统,可以追溯到以前,每个版本都以某种甜食命名。安卓14也不例外,但这一次的主题都是围绕太空构建的——还有一个复活节彩蛋。 安卓14复活节彩蛋实际上是一款很酷的小迷你游戏,你可以乘…...
国产32位单片机XL32F001,带1 路 12bit ADC,I2C、SPI、USART 等外设
XL32F001 系列单片机采用高性能的 32 位 ARM Cortex-M0内核,宽电压工作范围的 MCU。嵌入 24KbytesFlash 和 3Kbytes SRAM 存储器,最高工作频率 24MHz。包含多种不同封装类型多款产品。芯片集成 I2C、SPI、USART 等通讯外设,1 路 12bit ADC&am…...
typescript基础之null和undefined
TypeScript是一种基于JavaScript的编程语言,它支持静态类型检查和面向对象的特性。TypeScript中的null和undefined是两种基本类型,它们分别表示空值或未定义的值。在本文中,我将介绍TypeScript中null和undefined的含义、区别、检查方法和使用…...
php_mb_strlen指定扩展
1 中文在utf-字符集下占3个字节,所以计算出来长度为9。 2 可以引入php多字节字符的扩展,默认是没有的,需要自己配置这个函数 3 找到php.ini文件,去掉;extension mbstring的注释,接着重启apache服务 可以看到准确输出的中文的长度…...
利用OpenCV光流算法实现视频特征点跟踪
光流简介 光流(optical flow)是运动物体在观察成像平面上的像素运动的瞬时速度。光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。…...
探索无限创造力的星辰大道,画出想象的浩瀚宇宙!-turtle
介绍 视频教程地址在此:https://www.bilibili.com/video/BV1Pm4y1H7Tb/ 大家好,欢迎来到本视频!今天,我们将一同探索Python编程世界中的一个有趣而创意的库——Turtle库。无需专业绘画技能,你就可以轻松地用代码绘制…...
企业数字化转型大数据湖一体化平台项目建设方案PPT
导读:原文《企业数字化转型大数据湖一体化平台项目建设方案PPT》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 喜欢文章,您可以点赞评论转发…...
【3Ds Max】车削命令的简单使用(以制作花瓶为例)
简介 在3ds Max中,"车削"(Lathe)是一种建模命令,用于创建围绕轴线旋转的几何形状。通过车削命令,您可以将一个闭合的平面或曲线几何形状旋转,从而生成一个立体对象。这种方法常用于创建圆柱体、…...
Python 3 使用HBase 总结
HBase 简介和安装 请参考文章:HBase 一文读懂 Python3 HBase API HBase 前期准备 1 安装happybase库操作hbase 安装该库 pip install happybase2 确保 Hadoop 和 Zookeeper 可用并开启 确保Hadoop 正常运行 确保Zookeeper 正常运行3 开启HBase thrift服务 使用命…...
Maven方式构建SpringBoot项目
目录 1、创建maven项目 2、添加springboot相关依赖 3、配置启动端口 4、修改APP文件 5、配置controller 6、启动应用 1、创建maven项目 项目如下: 2、添加springboot相关依赖 <parent><groupId>org.springframework.boot</groupId><arti…...
不花一分钱,利用免费电脑软件将视频MV变成歌曲音频MP3
教程 1.点击下载电脑软件下载地址,点击下载,安装。(没有利益关系,没有打广告,只是单纯教学) 2.安装完成后,点击格式工厂 3.然后如图所示依次,点击【音频】->【-MP3】 3.然后点击…...
运营知识之用户运营(一)触达用户的几种方式
运营知识之用户运营(一)触达用户的几种方式 APP推送短信(DeepLink/Deferred DeepLink):短信拉起app电子邮件 EDM电话/外呼(人工、AI)电话外呼加短信(操作步骤短链)微信生…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...
