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)电话外呼加短信(操作步骤短链)微信生…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
基于 HTTP 的单向流式通信协议SSE详解
SSE(Server-Sent Events)详解 🧠 什么是 SSE? SSE(Server-Sent Events) 是 HTML5 标准中定义的一种通信机制,它允许服务器主动将事件推送给客户端(浏览器)。与传统的 H…...
vxe-table vue 表格复选框多选数据,实现快捷键 Shift 批量选择功能
vxe-table vue 表格复选框多选数据,实现快捷键 Shift 批量选择功能 查看官网:https://vxetable.cn 效果 代码 通过 checkbox-config.isShift 启用批量选中,启用后按住快捷键和鼠标批量选取 <template><div><vxe-grid v-bind"gri…...
Android Framework预装traceroute执行文件到system/bin下
文章目录 Android SDK中寻找traceroute代码内置traceroute到SDK中traceroute参数说明-I 参数(使用 ICMP Echo 请求)-T 参数(使用 TCP SYN 包) 相关文章 Android SDK中寻找traceroute代码 设备使用的是Android 11,在/s…...
【向量库】Weaviate 搜索与索引技术:从基础概念到性能优化
文章目录 零、概述一、搜索技术分类1. 向量搜索:捕捉语义的智能检索2. 关键字搜索:精确匹配的传统方案3. 混合搜索:语义与精确的双重保障 二、向量检索技术分类1. HNSW索引:大规模数据的高效引擎2. Flat索引:小规模数据…...
