当前位置: 首页 > news >正文

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 | 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;
}
  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、小结

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

相关文章:

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

C11并发与多线程笔记&#xff08;9&#xff09; async、future、packaged_task、promise 1、std::async、std::future创建后台任务并返回值2、std::packaged_task&#xff1a;打包任务&#xff0c;把任务包装起来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

问题再现&#xff1a; 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月全国计算机等级考试真题&#xff08;二级C语言&#xff09; 第1题 下列叙述中正确的是&#xff08; &#xff09;。 A. 算法的复杂度是指算法所处理的数据量 B. 算法的复杂度是指算法程序中指令的数量 C. 算法的复杂度是指算法控制结构的复杂程度 D. 算法的复杂度包…...

串口通讯

USART是全双工同步通讯 在同步通信中&#xff0c;数据信号所传输的内容绝大多数属于有效数据&#xff0c;而异步通信中包含了各种帧的标识符&#xff0c;所以同步通讯的效率更高。但是同步通信对时钟要求苛刻&#xff0c;允许的误差小。而异步通信则允许双方的误差较大 比特率…...

自动拉取 GitHub 仓库更新的脚本

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 由于将 HAUE-CS-WIKI 部署到了我自己的服务器上作为国内镜像站&#xff0c;每次在源站更新后都需要手动拉取镜像站的更新实在是太麻烦了&#xff0c;因此产生了编写该脚本的需求&#xff08; 读者可根据该…...

如何获得Android 14复活节彩蛋

每个新的安卓版本都有隐藏复活节彩蛋的悠久传统&#xff0c;可以追溯到以前&#xff0c;每个版本都以某种甜食命名。安卓14也不例外&#xff0c;但这一次的主题都是围绕太空构建的——还有一个复活节彩蛋。 安卓14复活节彩蛋实际上是一款很酷的小迷你游戏&#xff0c;你可以乘…...

国产32位单片机XL32F001,带1 路 12bit ADC,I2C、SPI、USART 等外设

XL32F001 系列单片机采用高性能的 32 位 ARM Cortex-M0内核&#xff0c;宽电压工作范围的 MCU。嵌入 24KbytesFlash 和 3Kbytes SRAM 存储器&#xff0c;最高工作频率 24MHz。包含多种不同封装类型多款产品。芯片集成 I2C、SPI、USART 等通讯外设&#xff0c;1 路 12bit ADC&am…...

typescript基础之null和undefined

TypeScript是一种基于JavaScript的编程语言&#xff0c;它支持静态类型检查和面向对象的特性。TypeScript中的null和undefined是两种基本类型&#xff0c;它们分别表示空值或未定义的值。在本文中&#xff0c;我将介绍TypeScript中null和undefined的含义、区别、检查方法和使用…...

php_mb_strlen指定扩展

1 中文在utf-字符集下占3个字节,所以计算出来长度为9。 2 可以引入php多字节字符的扩展&#xff0c;默认是没有的&#xff0c;需要自己配置这个函数 3 找到php.ini文件&#xff0c;去掉;extension mbstring的注释&#xff0c;接着重启apache服务 可以看到准确输出的中文的长度…...

利用OpenCV光流算法实现视频特征点跟踪

光流简介 光流&#xff08;optical flow&#xff09;是运动物体在观察成像平面上的像素运动的瞬时速度。光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系&#xff0c;从而计算出相邻帧之间物体的运动信息的一种方法。…...

探索无限创造力的星辰大道,画出想象的浩瀚宇宙!-turtle

介绍 视频教程地址在此&#xff1a;https://www.bilibili.com/video/BV1Pm4y1H7Tb/ 大家好&#xff0c;欢迎来到本视频&#xff01;今天&#xff0c;我们将一同探索Python编程世界中的一个有趣而创意的库——Turtle库。无需专业绘画技能&#xff0c;你就可以轻松地用代码绘制…...

企业数字化转型大数据湖一体化平台项目建设方案PPT

导读&#xff1a;原文《企业数字化转型大数据湖一体化平台项目建设方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 喜欢文章&#xff0c;您可以点赞评论转发…...

【3Ds Max】车削命令的简单使用(以制作花瓶为例)

简介 在3ds Max中&#xff0c;"车削"&#xff08;Lathe&#xff09;是一种建模命令&#xff0c;用于创建围绕轴线旋转的几何形状。通过车削命令&#xff0c;您可以将一个闭合的平面或曲线几何形状旋转&#xff0c;从而生成一个立体对象。这种方法常用于创建圆柱体、…...

Python 3 使用HBase 总结

HBase 简介和安装 请参考文章&#xff1a;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项目 项目如下&#xff1a; 2、添加springboot相关依赖 <parent><groupId>org.springframework.boot</groupId><arti…...

不花一分钱,利用免费电脑软件将视频MV变成歌曲音频MP3

教程 1.点击下载电脑软件下载地址&#xff0c;点击下载&#xff0c;安装。&#xff08;没有利益关系&#xff0c;没有打广告&#xff0c;只是单纯教学&#xff09; 2.安装完成后&#xff0c;点击格式工厂 3.然后如图所示依次&#xff0c;点击【音频】->【-MP3】 3.然后点击…...

运营知识之用户运营(一)触达用户的几种方式

运营知识之用户运营&#xff08;一&#xff09;触达用户的几种方式 APP推送短信&#xff08;DeepLink/Deferred DeepLink&#xff09;&#xff1a;短信拉起app电子邮件 EDM电话/外呼&#xff08;人工、AI&#xff09;电话外呼加短信&#xff08;操作步骤短链&#xff09;微信生…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...