C++11的异步操作让多线程开发变得简单

news2024/9/28 9:27:12

C++11的异步操作

  • 简介
  • 一、std::future
    • 1.1、future的类型
    • 1.2、future的使用
    • 1.3、使用示例
  • 二、std::packaged_task
  • 三、std::promise
  • 总结

简介

C++提供如下的异步操作接口:

  1. std::future :异步指向某个任务,然后通过future特性去获取任务函数的返回结果。
  2. std::aysnc :异步运行某个任务函数。 std::promise :线程1设置了某个值,然后通知另外的线程,可以省去消息通信。
  3. std::packaged_task :将任务和feature绑定在一起的模板,是一种对任务的封装。

一、std::future

std::future期待一个返回,从一个异步调用的角度来说,future更像是执行函数的返回值,C++标准库使用std::future为一次性事件建模,如果一个事件需要等待特定的一次性事件,那么这线程可以获取一个future对象来代表这个事件。

异步调用往往不知道何时返回,但是如果异步调用的过程需要同步,或者说后一个异步调用需要使用前一个异步调用的结果;这个时候就要用到future。

线程可以周期性的在这个future上等待一小段时间,检查future是否已经ready,如果没有,该线程可以先去做另一个任务,一旦future就绪,该future就无法复位(无法再次使用这个future等待这个事件),所以future代表的是一次性事件。

1.1、future的类型

在库的头文件中声明了两种future,唯一future(std::future)和共享future(std::shared_future)这两个是参照std::unique_ptr和std::shared_ptr设立的,前者的实例是仅有的一个指向其关联事件的实例,而后者可以有多个实例指向同一个关联事件,当事件就绪时,所有指向同一事件的std::shared_future实例会变成就绪。

future的三种用法:

  • 使用模板自己定义类型,比如std::future test=std::async(func); 。
  • 使用decltype推导函数返回类型,比如std::future<decltype (func())> test=std::async(func);。
  • 使用auto,比如auto test=std::async(func);。

注意,使用decltype推导函数的返回类型,如果函数是带参数的,需要传入函数的参数值,而不是参数类型。

std::future<decltype (func(int,int))> test=std::async(func,1,2);//错误的
std::future<decltype (func(00))> test=std::async(func,1,2);//正确的

1.2、future的使用

std::future是一个模板,例如std::future,模板参数就是期待返回的类型,虽然future被用于线程间通信,但其本身却并不提供同步访问,热门必须通过互斥元或其他同步机制来保护访问。

future使用的时机是当你不需要立刻得到一个结果的时候,你可以开启一个线程帮你去做一项任务,并期待这个任务的返回,但是std::thread并没有提供这样的机制,这就需要用到std::async和std::future(都在头文件中声明)std::async返回一个std::future对象,而不是给你一个确定的值(所以当你不需要立刻使用此值的时候才需要用到这个机制)。当你需要使用这个值的时候,对future使用get(),线程就会阻塞直到future就绪,然后返回该值。

1.3、使用示例

#include <iostream>
#include <future>
#include <thread>
using namespace std;

int find_result_to_add()
{
        std::this_thread::sleep_for(std::chrono::seconds(2));// 用来测试异步延迟的影响
        return 1+1;
}

int find_result_to_add2(int a,int b)
{
        std::this_thread::sleep_for(std::chrono::seconds(2));
        return a+b;
}

void do_other_things()
{
        std::cout << "Hello World" << std::endl;
}
int main(int argc,char **argv)
{
        // 1
        future<int> result=std::async(find_result_to_add);
        do_other_things();
        std::cout << "result: " << result.get() << std::endl;
        // 2
        future<decltype (find_result_to_add2(0,0))> result2=async(find_result_to_add2,10,20);

        do_other_things();
        std::cout << "result2: " << result2.get() << std::endl;
        // 3
        auto result3=async(find_result_to_add2,10,20);//推荐
        std::cout << "result3: " << result3.get() << std::endl;
        return 0;
}

运行结果:

Hello World
result: 2
Hello World
result2: 30
result3: 30

跟thread类似,async允许你通过将额外的参数添加到调用中,来将附加参数传递给函数。如果传入的函数指针是某个类的成员函数,则还需要将类对象指针传入(直接传入,传入指针,或者是std::ref封装)。
默认情况下,std::async是否启动一个新线程,或者在等待future时,任务是否同步运行都取决于你给的参数。这个参数为std::launch类型:

  • std::launch::defered,表明该函数会被延迟调用,直到在future上调用get()或者wait()为止 。
  • std::launch::async,表明函数会在自己创建的线程上运行 。
  • std::launch::any = std::launch::defered | std::launch::async 。
  • std::launch::sync = std::launch::defered 。
enum class launch { 
    async,deferred,sync=deferred,any=async|deferred 
};

注意:默认选项参数被设置为std::launch::any。如果函数被延迟运行可能永远都不会运行。

二、std::packaged_task

如果说std::async和std::feature还是分开看的关系的话,那么std::packaged_task就是将任务和feature绑定在一起的模板,是一种封装对任务的封装。

std::packaged_task包装任何可调用目标(函数、lambda表达式、绑定表达式或其他函数对象),以便可以异步调用它。它的返回值或抛出的异常存储在共享状态中,可以通过std::future对象访问。

可以通过std::packaged_task对象获取任务相关联的feature,调用get_future()方法可以获得std::packaged_task对象绑定的函数的返回值类型的future。std::packaged_task的模板参数是函数签名(例如int add(int a, intb)的函数签名就是int(int, int) )。

使用示例:

#include <iostream> 
#include <future> 
using namespace std; 
int add(int a, int b) 
{ 
	return a + b; 
}
void do_other_things() {
	std::cout << "Hello World" << std::endl; 
}
int main() 
{ 
	std::packaged_task<int(int, int)> task(add); //封装任务,不会执行add(...)
	std::this_thread::sleep_for(std::chrono::seconds(2));// 用来测试异步延迟的影响
	do_other_things(); 
	std::future<int> result = task.get_future(); // 只是获取future
	task(1, 1);                            // 必须要让任务执行,否则在get()获取future的值时会一直阻塞 
	std::cout << result.get() << std::endl; 
	return 0; 
}

三、std::promise

从字面意思上理解promise代表一个承诺。promise比std::packaged_task抽象层次低。
std::promise提供了一种设置值的方式,它可以在这之后通过相关联的std::future对象进行读取。换种说法,之前已经说过std::future可以读取一个异步函数的返回值了,那么这个std::promise就提供一种方式手动让future就绪。

使用示例:

#include <iostream> 
#include <future> 
#include <string>
#include <pthread>

using namespace std; 

void print(std::promise<std::string>& p)
{ 
	p.set_value("There is the result whitch you want."); //set_value之后,就可以get()到
}
void do_some_other_things() 
{ 
	std::cout << "Hello World" << std::endl; 
}
int main() 
{ 
	std::promise<std::string> promise; 
	std::future<std::string> result = promise.get_future(); 
	std::thread t(print, std::ref(promise)); 
	do_some_other_things(); 
	std::cout << result.get() << std::endl; //等待promise的返回
	t.join(); 
	return 0; 
}

由此可以看出在promise创建好的时候future也已经创建好了线程在创建promise的同时会获得一个future,然后将promise传递给设置他的线程,当前线程则持有future,以便随时检查是否可以取值。

总结

future的表现为期望,当前线程持有future时,期望从future获取到想要的结果和返回,可以把future当做异步函数的返回值。而promise是一个承诺,当线程创建了promise对象后,这个promise对象向线程承诺他必定会被人设置一个值,和promise相关联的future就是获取其返回的手段。

在这里插入图片描述

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

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

相关文章

1. RNN神经网络初探

目录1. 神经网络与未来智能2. 回顾数据维度和神经网络3. 文本转变为词向量1. 神经网络与未来智能 2. 回顾数据维度和神经网络 循环神经网络&#xff0c;主要用来处理时序的数据&#xff0c;它对每个词的顺序是有要求的。 循环神经网络如何保存记忆功能&#xff1f; 当前样本只…

Window10开放某个端口

需求&#xff1a;由于防火墙原因&#xff0c;开放某个端口:如9999 在开始那里搜索防火墙-进入防火墙 第一步&#xff1a;核实是否启动了防火墙&#xff0c;之后进行 第二步&#xff1a;点击“高级设置”&#xff0c;→“入站规则”→“新建规则”→“端口”→ “下一步” …

【前端领域】3D旋转超美相册(HTML+CSS)

世界上总有一半人不理解另一半人的快乐。 ——《爱玛》 目录 一、前言 二、本期作品介绍 3D旋转相册 三、效果展示 四、详细介绍 五、编码实现 index.html style.css img 六、获取源码 公众号获取源码 获取源码&#xff1f;私信&#xff1f;关注&#xff1f;点赞&…

基于微信小程序的游戏账号交易小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

【C++初阶】十二、STL---反向迭代器的实现

目录 一、反向迭代器 二、反向迭代器的实现 一、反向迭代器 之前的模拟实现vector、list 的时候&#xff0c;这些都是实现了正向迭代器&#xff0c;反向迭代器都没有实现&#xff0c;这里就要实现反向迭代器 反向迭代器也是适配器&#xff08;配接器&#xff09;的一种&#…

在阿里干了2年的测试,总结出来的划水经验

测试新人 我的职业生涯开始和大多数测试人一样&#xff0c;开始接触都是纯功能界面测试。那时候在一家电商公司做测试&#xff0c;做了一段时间&#xff0c;熟悉产品的业务流程以及熟练测试工作流程规范之后&#xff0c;效率提高了&#xff0c;工作比较轻松&#xff0c;这样我…

代码随想录第55天(动态规划):● 309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费

一、最佳买卖股票时机含冷冻期 题目描述: 思路和想法&#xff1a; 这道题相较于之前的题目&#xff0c;注重对状态的分析&#xff0c;这里分为四个状态。 &#xff08;1&#xff09;状态一&#xff0c;买入状态 dp[i][0] 操作一&#xff1a;前一天就是持有状态&#xff08;状…

day39【代码随想录】动态规划之

文章目录前言一、不同路径&#xff08;力扣62&#xff09;二、不同路径||&#xff08;力扣63&#xff09;三、最小路径和&#xff08;力扣64&#xff09;前言 1、不同路径 2、不同路径|| 3、最小路径和 一、不同路径&#xff08;力扣62&#xff09; 一个机器人位于一个 m x n…

MyBatis-Plus分页插件和MyBatisX插件

MyBatis-Plus分页插件和MyBatisX插件六、插件1、分页插件a>添加配置类b>测试八、代码生成器1、引入依赖2、快速生成十、MyBatisX插件1、新建spring boot工程a>引入依赖b>配置application.ymlc>连接MySQL数据库d>MybatisX逆向生成2、MyBatisX快速生成CRUD申明…

学习open62541 --- [74] 软链接的妙用

一 原理 在同一台电脑里&#xff0c;可能会有多个工程同时用到open62541&#xff0c;比较简单的办法是每个工程都拷贝一份open62541源码&#xff0c;但是这样会造成空间浪费&#xff0c;而且open62541的源码包本身也不小。 对于Linux用户来说&#xff0c;可以使用软链接来解决…

【Python学习笔记】28.Python3 错误和异常

前言 作为 Python 初学者&#xff0c;在刚学习 Python 编程时&#xff0c;经常会看到一些报错信息&#xff0c;在前面我们没有提及&#xff0c;这章节我们会专门介绍。 Python3 错误和异常 Python 有两种错误很容易辨认&#xff1a;语法错误和异常。 Python assert&#xf…

德国奔驰、博世和保时捷的员工年薪有多少?

点击 欧盟IT那些事 关注我们公告&#xff1a;因企鹅审核规定&#xff0c;本公众号从《德国IT那些事》更名为《欧盟IT那些事》。从职场新人到总裁&#xff0c;一个个盘。位于德国斯图加特的梅赛德斯-奔驰集团及其子公司梅赛德斯-奔驰是世界最知名的汽车制造商之一。奔驰车代表着…

大数据之HBase高级

文章目录前言一、HBase的架构&#xff08;一&#xff09;Client&#xff08;二&#xff09;Master Server&#xff08;三&#xff09;Region Server二、HBase的工作原理&#xff08;一&#xff09;存储数据流程&#xff08;二&#xff09;读取数据流程&#xff08;三&#xff0…

《计算机组成与设计》01. 计算机抽象及相关技术

文章目录计算机体系结构中的 8 个伟大思想面向摩尔定律的设计使用抽象简化设计加速经常性事件通过并行提高性能通过流水线提高性能存储层次通过冗余提高可靠性性能性能的度量时钟周期数和时钟周期长度与CPU时间的公式指令性能公式经典的 CPU 性能公式CPI 计算公式程序执行时间计…

微前端基础

一、什么是微前端 微前端是一种软件架构&#xff0c;可以将前端应用拆解成一些更小的能够独立开发部署的微型应用&#xff0c;然后再将这些微应用进行组合使其成为整体应用的架构模式。微前端架构类似于组件架构&#xff0c;但不同的是&#xff0c;组件不能独立构建和发布&…

7 MMSegmentation 代码教学

本文是openmmlab AI实战营的第七次课程的笔记&#xff0c;以下是我比较关注的部分。本次课程的主要内容是 mmsegmentation的代码教学实战&#xff0c;我会稍微详细记载。环境安装云在线平台 &#xff1a;Featurize推荐代码运行环境&#xff1a;GPU RTX3060 ,CUDA 11.2安装pytor…

[SSD固态硬盘技术 0] SSD的结构和原理导论

版权声明&#xff1a; 本文禁止转载机械硬盘的存储系统由于内部结构,其IO访问性能无法进一步提高,CPU与存储器之间的性能差距逐渐扩大。以Nand Flash为存储介质的固态硬盘技术的发展&#xff0c;性能瓶颈得到缓解。1. 什么是SSD固态硬盘&#xff08;Solid State Drives&#xf…

Python爱心代码

前言 Python漂浮爱心&#xff0c;具体源码见&#xff1a;Python动态爱心代码_爱心代码-Python文档类资源-CSDN下载 爱心类 class Heart(): #每个爱心&#xff08;爱心类&#xff09; def __init__(self): self.r ra.randint(10,15) #爱心的半径 …

[LeetCode 1138]字母板上的路径

题目描述 题目链接&#xff1a;[LeetCode 1138]字母板上的路径 我们从一块字母板上的位置 (0, 0) 出发&#xff0c;该坐标对应的字符为 board[0][0]。 在本题里&#xff0c;字母板为board [“abcde”, “fghij”, “klmno”, “pqrst”, “uvwxy”, “z”]&#xff0c;如下所…

图形编辑器:绘制图形需要用到的填充算法

大家好&#xff0c;我是前端西瓜哥。今天我们来谈谈图形编辑器中&#xff0c;简单说说图形编辑实现图形工具&#xff0c;需要用到的填充算法。 图形的特点是宽高是固定的&#xff0c;在图形编辑器绘制图片有两种方案。 一种是将 宽高比锁死&#xff0c;不允许改变&#xff0c…