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类型枚举类型),来达到一些特殊的目的:

  1. 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;
}

在这里插入图片描述

  1. std::launch::async,在调用async函数的时候就开始创建新线程,不添加标记,默认用的就是std::launch::async标记。
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;
}
  1. 同时使用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、小结

我们学习这些东西的目的并不是,要把他们都用到实际开发中。
相反,如果我们能够用最少的东西写出一个稳定的,高效的多线程程序,更值得赞赏。
我们为了成长必须阅读一些高手写的代码,从而实现自己代码的积累;

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

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

相关文章

哈工大开源“活字”对话大模型

一、介绍 大规模语言模型&#xff08;LLM&#xff09;在自然语言处理的通用领域已取得了令人瞩目的成功。对于广泛的应用场景&#xff0c;这种技术展示了强大的潜力&#xff0c;学术界和工业界的兴趣也持续升温。哈工大自然语言处理研究所30余位老师和学生参与开发了通用对话大…

re学习(35)攻防世界-no-strings-attached(动调)

参考文章&#xff1a;re学习笔记&#xff08;28&#xff09;攻防世界-re-no-strings-attached_Forgo7ten的博客-CSDN博客 攻防世界逆向入门题之no-strings-attached_攻防世界 no-strings-attached_沐一 林的博客-CSDN博客 本人题解&#xff1a; 扔入Exepeinfo中查壳和其他信息…

LVS-DR模型实例

一、LVS-DR集群介绍 LVS-DR&#xff08;Linux Virtual Server Director Server&#xff09;工作模式&#xff0c;是生产环境中最常用的一 种工作模式。 1、LVS-DR 工作原理 LVS-DR 模式&#xff0c;Director Server 作为群集的访问入口&#xff0c;不作为网关使用&#xff0…

python+django+mysql项目实践五(信息搜索)

python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 信息搜素 输入内容进行搜索,内容有文本类和时间类 文本类需要模糊搜索,包含即检索 时间类需要选取时间范围内的内容 views 利用Q完成对指定内容的检索 检索后按检索内容更新…

HarmonyOS/OpenHarmony应用开发-ArkTS语言渲染控制ForEach循环渲染

ForEach基于数组类型数据执行循环渲染。说明&#xff0c;从API version 9开始&#xff0c;该接口支持在ArkTS卡片中使用。 一、接口描述 ForEach(arr: any[], itemGenerator: (item: any, index?: number) > void,keyGenerator?: (item: any, index?: number) > stri…

网络综合布线实训室建设方案

一、网络综合布线系统概述 网络综合布线系统是为了满足数据通信需求而设计和建立的一套基础设施。它提供了数据传输、信号传输和电力供应的基础结构&#xff0c;支持各种网络设备和终端设备之间的连接。 网络综合布线系统通常包括以下组成部分&#xff1a; 1&#xff09; 数据…

ZooKeeper集群服务器启动

在本文中&#xff0c;我们将对集群版ZooKeeper服务器的启动过程做详细讲解。集群和单机ZooKeeper服务器的启动过程在很多地方都是一致的&#xff0c;因此本节只会对有差异的地方展开进行讲解。下图所示是集群版ZooKeeper服务器的启动流程图。 预启动 预启动的步骤如下。 (1)统…

Python高光谱遥感数据处理与高光谱遥感机器学习方法教程

详情点击链接&#xff1a;Python高光谱遥感数据处理与高光谱遥感机器学习方法教程 第一&#xff1a;高光谱基础 一&#xff1a;高光谱遥感基本 01)高光谱遥感 02)光的波长 03)光谱分辨率 04)高光谱遥感的历史和发展 二&#xff1a;高光谱传感器与数据获取 01)高光谱遥感…

AI搜索引擎助力科学家创新

开发者希望通过帮助科学家从大量文献中发现联系从而解放科学家&#xff0c;让他们专注于发现和创新。 图片来源&#xff1a;The Project Twins 对于专注于历史的研究者Mushtaq Bilal来说&#xff0c;他在未来科技中投入了大量时间。 Bilal在丹麦南部大学&#xff08; Universit…

预训练GNN:GPT-GNN Generative Pre-Training of Graph Neural Networks

一.文章概述 本文提出了一种自监督属性图生成任务来预训练GNN&#xff0c;使得其能捕图的结构和语义属性。作者将图的生成分为两个部分&#xff1a;属性生成和边生成&#xff0c;即给定观测到的边&#xff0c;生成节点属性&#xff1b;给定观测到的边和生成的节点属性&#xf…

自动驾驶港口车辆故障及事故处理机制

1、传感器故障&#xff1a; &#xff08;1&#xff09;单一传感器数据异常处理。自动驾驶电动平板传感方案为冗余设置&#xff0c;有其他传感器能够覆盖故障传感器观测区域&#xff0c;感知/定位模块将数据异常情况发给到规划决策模块&#xff0c;由“大脑”向中控平台上报故障…

视频集中存储/云存储/磁盘阵列EasyCVR平台接入RTSP设备出现离线情况的排查

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

QT处理日志文件

由于实际生产需要&#xff0c;软件系统的运行&#xff0c;会产生大量的日志文件&#xff0c;有时候一天就能产生超过百万条log记录&#xff0c;那么为了能够处理日志文件&#xff0c;查询并且找到我们想要的报错信息&#xff0c;因此不得不考虑怎么实现&#xff0c;打开大日志文…

ARM--day2(cpsr、spsr、数据搬移指令、移位操作指令、位运算操作指令、算数运算指令、比较指令、跳转指令)

.text .global _gcd _gcd:mov r0,#9mov r1,#15b loop loop:cmp r0,r1beq stopsubhi r0,r1bhi loopsubcc r1,r0bcc loopstop:b stop.end用for循环实现1~100之间和5050 .text .global _gcd _gcd:mov r0,#0x0mov r1,#0x1mov r2,#0x64b loop loop:cmp r1,r2bhi stopadd r0,r0,r1ad…

【Unity】坐标转换经纬度方法(应用篇)

【Unity】坐标转换经纬度方法&#xff08;应用篇&#xff09; 解决地图中经纬度坐标转换与unity坐标互转的问题。使用线性变换的方法&#xff0c;理论上可以解决小范围内所以坐标转换的问题。 之前有写过[Unity]坐标转换经纬度方法&#xff08;原理篇),在实际使用中&#xff0c…

R语言实现免疫浸润分析(2)

原始数据承接免疫浸润分析&#xff08;1&#xff09;&#xff0c;下面展示免疫浸润结果&#xff1a; #直接使用IOBR包内的cell_bar_plot pic<-cell_bar_plot(input quantiseq_immo_de[1:20,], title "quanTiseq Cell Fraction") #使用ggplot2 library(ggplot2)…

大疆飞卡30运载无人机技术分享

大疆飞卡30是大疆公司面向运输领域推出的一款专业运载无人机。它采用了优秀的设计,装备了多种先进传感器,以解决运输中的难题。以下我们来了解一下其主要特点: 【应用领域】 飞卡30适用于山地救灾、农业化肥施用、工程材料运送等交通不便的山区应用,也适用于海岛联通等运输链…

机器学习基础之《分类算法(3)—模型选择与调优》

作用是如何选择出最好的K值 一、什么是交叉验证&#xff08;cross validation&#xff09; 1、定义 交叉验证&#xff1a;将拿到的训练数据&#xff0c;分为训练和验证集。以下图为例&#xff1a;将数据分成5份&#xff0c;其中一份作为验证集。然后经过5次(组)的测试&#x…

通过请求头传数据向后端发请求

axios &#xff08;get post请求、头部参数添加&#xff09;傻瓜式入门axios_axiospost请求参数_web_blog的博客-CSDN博客

ByteBuffer 使用

ByteBuffer 使用 1 java.nio包中的类定义的缓冲区类型2 缓冲区常用属性2.1缓冲区的容量(capacity)2.2 缓冲区的位置(position)2.3 缓冲区的限制(limit)2.4 缓冲区的标记(mark)2.5 剩余容量 remaining/hasRemaining 3 缓冲区常用方法3.1 创建缓冲区3.1.1 allocate方法3.1.2 wrap…