55 C++ 多线程 返回值问题。引出的 async,future,packaged_task,promise.

一 前提,thread返回值的写法

在之前的代码中,我们并没有讨论 子线程的返回值问题。

这一章就考虑这个问题怎么处理。

下面我们先按照之前的写法,我们需要返回值时的可能的fix方案。

//如果线程有返回值,并且主线程要得到这个返回值,

//方案一:使用全局变量去接这个子线程的返回值。然后在main函数中取。


//如果线程有返回值,并且主线程要得到这个返回值,//方案一:使用全局变量去接这个子线程的返回值。然后在main函数中取。
int teacher176readfuncreturnnumber = -1;
int teacher176writefuncreturnnumber = -1;class Teacher176 {
public:mutex mymutex;list<int> mylist;public:int readfunc(string &tempstr) {for (size_t i = 0; i < 10; i++){mymutex.lock();cout << "read func tempstr = " << tempstr << "  i = " << i << " threadid = " << this_thread::get_id()<<  "  &tempstr = " << &tempstr <<  "  tempstr = " << tempstr << endl;mymutex.unlock();}int tempreturnvalue = 10;teacher176readfuncreturnnumber = tempreturnvalue;return tempreturnvalue;}int writefunc(double &tempdouble) {for (size_t i = 0; i < 10; i++){mymutex.lock();cout << "write func tempdouble = " << tempdouble << "  i = " << i <<" threadid = " << this_thread::get_id() <<  "  &tempdouble = " << &tempdouble <<"   tempdouble = " << tempdouble << endl;mymutex.unlock();}tempdouble = tempdouble * 2;teacher176writefuncreturnnumber = (int)tempdouble;return (int)tempdouble;}
};void main() {cout << "main thread start " << endl;Teacher176 tea;string name = "nihao";double dou = 60.8;cout << "main threadid = " << this_thread::get_id() << " &name = " << &name << "   name = " << name << "  dou = " << dou << "   &dou = " << &dou << endl;thread readthread(&Teacher176::readfunc,&tea, ref(name));//注意要第二个传递的 &tea,不会调用copy构造函数。注意第三个参数,如果不用ref包裹,那么传递的也是copy,即使readfunc的参数是引用,thread内部也会搞一个copy,且readfunc的参数必须是const的。thread writethread(&Teacher176::writefunc, &tea, ref(dou));readthread.join();writethread.join();cout << "teacher176readfuncreturnnumber = " << teacher176readfuncreturnnumber <<  "  teacher176writefuncreturnnumber = " << teacher176writefuncreturnnumber << endl;cout << "main thread end" << endl;
}

从上述代码中,虽然可以获得子线程的返回值,但是这样并不方便。

C++给考虑到这种case,提供了 async函数,返回值是 future的方案。

二 async函数模版,启动一个异步任务,返回值用 future 来接。

在标头 <future> 定义

2.1 async是干啥的?

async 是一个函数模版,用来启动一个异步任务。

注意这个异步任务可能是在子线程中启动,也可能不是在子线程中启动。这由 async的参数决定

//(C++11 起)(C++17 前)
//template< class Function, class... Args>
//std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> async(Function&& f, Args&&... args);  

2.2 async的返回值future。从future中可以获得 异步任务的返回值

从C++文档上来看,返回值是个类对象-future,

std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> 

这个这个future里面有方法可以获得 返回值。

future中有用的成员函数

(构造函数)

构造 future 对象
(公开成员函数)

(析构函数)

析构 future 对象
(公开成员函数)

operator=

移动future对象
(公开成员函数)

share

*this 转移共享状态给 shared_future 并返回它
(公开成员函数)

获取结果

get

返回结果
(公开成员函数)

状态

valid

检查 future 是否拥有共享状态
(公开成员函数)

wait

等待结果变得可用
(公开成员函数)

wait_for

等待结果,如果在指定的超时间隔后仍然无法得到结果,则返回。
(公开成员函数)

wait_until

等待结果,如果在已经到达指定的时间点时仍然无法得到结果,则返回。
(公开成员函数)

2.3 async的返回值什么时候能拿到?--- 在异步任务执行完毕的时候

2.4 async方法参数详解

2.4.1 不带标志位。等同于 ( std::launch::async| std::launch::deferred )

如下两行代码功能相同

    future<int> fu = async(std::launch::async | std::launch::deferred ,&Teacher177::readfunc, &tea, ref(name));
    future<int> fu = async(&Teacher177::readfunc, &tea, ref(name));

意思是:

会启动一个异步任务,这个异步任务有可能在子线程中发生,也有可能是在调用这段code的线程中发生(在当前code中是在主线程发生)。

那么为什么C++编译器要这么干呢?这是因为在创建一个thread的时候,有可能会创建thread失败,如果创建thread失败,系统直接就崩溃了。

但是如果是async,会根据当前系统的繁忙程度,决定到底要不要create 一个线程来完成这个异步任务。

class Teacher177 {
public:mutex mymutex;list<int> mylist;public:int readfunc(string &tempstr) {for (size_t i = 0; i < 10; i++){mymutex.lock();cout << "read func tempstr = " << tempstr << "  i = " << i << " threadid = " << this_thread::get_id() << "  &tempstr = " << &tempstr << "  tempstr = " << tempstr << endl;mymutex.unlock();}return 10;}int writefunc(const int &tempdouble) {for (size_t i = 0; i < 10; i++){mymutex.lock();cout << "write func tempdouble = " << tempdouble << "  i = " << i << " threadid = " << this_thread::get_id() << "  &tempdouble = " << &tempdouble << "   tempdouble = " << tempdouble << endl;mymutex.unlock();}//tempdouble = tempdouble * 2;return (int)tempdouble;}public:Teacher177() {cout << "Teacher177 构造方法被执行" << endl;}~Teacher177() {cout << "Teacher177 析构方法被执行" << endl;}Teacher177(const Teacher177& tea) {cout << "Teacher177 copy 构造 被执行" << endl;}
};void main() {Teacher177 tea;string name = "nihao";int dou = 300;cout << "main threadid = " << this_thread::get_id() << " &name = " << &name << "   name = " << name << "  dou = " << dou << "   &dou = " << &dou << endl;future<int> fu = async(std::launch::async | std::launch::deferred ,&Teacher177::readfunc, &tea, ref(name));//future<int> fu = async(&Teacher177::readfunc, &tea, ref(name));future<int> fu1 = async(std::launch::async | std::launch::deferred ,&Teacher177::writefunc, &tea, dou);//future<int> fu1 = async(&Teacher177::writefunc, &tea, dou);cout<< fu.get() <<endl;cout<< fu1.get() <<endl;cout << "endsss" << endl;//Teacher177 构造方法被执行//	main threadid = 14384 & name = 000000EEBB73F778   name = nihao  dou = 300 & dou = 000000EEBB73F7B4//	read func tempstr = nihao  i = 0 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 1 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 2 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 3 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 4 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 5 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 6 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 7 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 8 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	read func tempstr = nihao  i = 9 threadid = 6124 & tempstr = 000000EEBB73F778  tempstr = nihao//	write func tempdouble = 300  i = 0 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	10//	write func tempdouble = 300  i = 1 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 2 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 3 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 4 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 5 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 6 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 7 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 8 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	write func tempdouble = 300  i = 9 threadid = 14380 & tempdouble = 00000201C9F947A8   tempdouble = 300//	300//	endsss//	Teacher177 析构方法被执行
}

2.4.2 带标志位  std::launch::async

    future<int> fu = async(std::launch::async ,&Teacher177::readfunc, &tea, ref(name));

如果您的业务逻辑一定要在子线程完成,那么请清晰的加上 std::launch::async 这个标识符,当然,如果系统很忙的时候,当执行到这一行代码的时候,也会create thread,也会崩溃。

2.4.3 带标志位  std::launch::deferred

future<int> fu = async(std::launch::deferred,&Teacher177::readfunc, &tea, ref(name));

如果是deferred,表示会延迟启动这个异步任务。

那么延迟到什么时候启动这个任务呢?--延迟到使用结果 fu.get()的时候,

而且通过代码实验,我们观察到,如果是deferred,不会在子线程启动异步任务,而是在 当前线程。

主线程threadid = 11008

其他两个异步任务的threadid也是11008

main threadid = 11008 &name = 0000003519CFF768   name = nihao  dou = 300   &dou = 0000003519CFF7A4
read func tempstr = nihao  i = 0 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 1 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 2 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 3 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 4 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 5 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 6 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 7 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 8 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
read func tempstr = nihao  i = 9 threadid = 11008  &tempstr = 0000003519CFF768  tempstr = nihao
10
write func tempdouble = 300  i = 0 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 1 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 2 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 3 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 4 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 5 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 6 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 7 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 8 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
write func tempdouble = 300  i = 9 threadid = 11008  &tempdouble = 0000020FD3DC41E8   tempdouble = 300
300
endsss
Teacher177 析构方法被执行

2.5 问题:async在不加参数的时候,由系统决定是 std::launch::deferred还是std::launch::async,那么这里有一个问题了,我们怎么知道系统这时候 是执行的那种呢?

 需要使用 wait_for参数,传递0,就可以马上知道

	//再没有参数(launch::async或者 launch::deferred)的情况下,我们怎么马上知道这行代码会使用那种模式运行呢?需要使用 wait_for参数,传递0promise<int> promi5;shared_future<double> fu9 = async(promisefunc181, ref(promi5), ref(str3));future_status fustatusfu9 = fu9.wait_for(chrono::seconds(0));if (future_status::deferred == fustatusfu9) {cout << "此行代码使用deferred模式运行" << endl;//如果这行代码一定要运行,在当前线程运行也可以那么这时候,你要使用get()方法让其运行fu9.get();//如果这行代码还必须要在子线程完成。那么就要写在子线程,这时候因为promi5已经用过了,就要重新弄一个promi6promise<int> promi6;async(launch::async,promisefunc181, ref(promi6), ref(str3));}else if(future_status::ready == fustatusfu9){cout << "此行代码使用async模式运行,且已经运行完毕,可以从future中get值了" << endl;double dou = fu9.get();cout << "douready = " << dou << endl;}else if (future_status::timeout == fustatusfu9) {cout << "此行代码使用async模式运行,但是超时了,可以调用 future中get中重新获得" << endl;double dou = fu9.get(); //调用 fu9.get(),去重新调用方法cout << "doutimeout = " << dou << endl;}

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

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

相关文章

《WebKit 技术内幕》之五(3): HTML解释器和DOM 模型

3 DOM的事件机制 基于 WebKit 的浏览器事件处理过程&#xff1a;首先检测事件发生处的元素有无监听者&#xff0c;如果网页的相关节点注册了事件的监听者则浏览器会将事件派发给 WebKit 内核来处理。另外浏览器可能也需要处理这样的事件&#xff08;浏览器对于有些事件必须响应…

logstack 日志技术栈-04-opensource 开源工具 OpenObserve+Grafana Loki

日志技术栈 日志管理包含日志数据存储、处理、分析和可视化&#xff0c;通过利用日志管理工具&#xff0c;可以监控性能趋势、解决问题、检测异常并优化整体系统性能。 近年来&#xff0c;开源日志管理解决方案在大家寻求灵活且经济有效的方式来管理现代系统典型的大量日志数…

JVM问题分析处理手册

一.前言 各位开发和运维同学&#xff0c;在项目实施落地的过程中&#xff0c;尤其是使用EDAS、DRDS、MQ这些java中间件时&#xff0c;肯定会遇到不少JAVA程序运行和JVM的问题。我结合过去遇到的各种各样的问题和实际处理经验&#xff0c;总结了JAVA问题的处理方式&#xff0c;…

soso移动营业大厅(纯后端+MySQL数据库+JDBC)

一、项目需求 中国移动,中国联通,中国电信是国内3大通信运营商,每个运营商都提供了不同的品牌套餐来应对不同的用户群,比如北京移动主要有全球通,神州行,动感地带等3大品牌套餐,每种套餐的内容和费用不同,嗖嗖移动是一个假定的通信运营商,提供了话痨套餐,网虫套餐,超人套餐,各…

等离子环制作

免责声明 在您参考该博客制作等离子环前&#xff0c;请仔细阅读以下重要安全警告和免责说明。使用本文档即表示您已充分了解并同意以下条款&#xff1a; 等离子环的危险性&#xff1a;等离子环在运行时玻璃瓶身会产生高温&#xff0c;存在低温烧伤风险。任何时候都不建议用手…

C for Graphic:Sliced Circle Image

不做UI不知道&#xff0c;没想到时至今日&#xff0c;ugui居然没有sliced filled image模式&#xff0c;用circle做filled&#xff0c;不能用sliced九宫格图&#xff0c;导致每次使用这个效果必须一张新图&#xff0c;何其浪费资源。 原始功能如下&#xff1a; 我…

contextlib.contextmanager函数装饰器介绍

contextlib 是 Python 标准库中的一个模块&#xff0c;提供了一些用于创建上下文管理器&#xff08;context manager&#xff09;的实用工具。contextlib.contextmanager 是 contextlib 模块中的一个装饰器&#xff0c;用于将一个生成器函数转换为上下文管理器。通过使用这个装…

LC 2788. 按分隔符拆分字符串

2788. 按分隔符拆分字符串 难度 简单 题目大意&#xff1a; 给你一个字符串数组 words 和一个字符 separator &#xff0c;请你按 separator 拆分 words 中的每个字符串。 返回一个由拆分后的新字符串组成的字符串数组&#xff0c;不包括空字符串 。 注意 separator 用于决…

Go语言协程使用

主协程执行打印&#xff0c;子协程不打印 package main import ("fmt" )func do(i int) {fmt.Println("执行中") } func main() {fmt.Println("main协程")go do(1)fmt.Println("执行完了") }//main协程 //执行完了子协程没有打印输出…

椋鸟C语言笔记#36:从源代码到运行

萌新的学习笔记&#xff0c;写错了恳请斧正。 目录 从源代码到运行 翻译环境 编译 预处理 编译 汇编 链接 运行环境 从源代码到运行 在ANSI C的标准中&#xff0c;源代码先经过翻译环境生成可执行程序&#xff0c;再于运行环境中执行 翻译环境 翻译环境由编译与链接…

【C++】unordered_map,unordered_set模拟实现

unordered_map&#xff0c;unordered_set模拟实现 插入普通迭代器const迭代器unordered_map的[ ]接口实现查找修改哈希桶完整代码unordered_map完整代码unordered_set完整代码 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 上一篇文章我们把unordered_map和u…

爬虫之Cookie获取:利用浏览器模拟一个cookie出来、面对反爬虫、加密的cookie的应对方法

爬虫之Cookie获取&#xff1a;利用浏览器模拟一个cookie出来、面对反爬虫、加密的cookie的应对方法 在爬虫或模拟请求时&#xff0c;特别是获取验证码的时候&#xff0c;反爬虫的网站的cookie或定期失效&#xff0c;复制出来使用是不行的为了应对这种方式&#xff0c;我们可能…

第十一站:多态练习ODU

实现动态切换 ODU.h #pragma once #include <iostream> using namespace std; #define ODU_TYPE_311_FLAG "311" #define ODU_TYPE_335_FLAG "335" enum class ODU_TYPE {ODU_TYPE_311,ODU_TYPE_335,ODU_TYPE_UNKNOW };class ODU{ public:ODU();//发…

Windows下载安装vcpkg并使用它来安装第三方库(visualstudio)

1.使用Git下载vcpkg仓库&#xff08;下载比较慢&#xff0c;个人比较喜欢打开下面网址然后用迅雷下载&#xff0c;速度飞快&#xff09; git clone "https://github.com/Microsoft/vcpkg.git"2.下载好之后解压打开文件夹&#xff0c;双击bootstrap-vcpkg.bat文件&…

中小企业如何快速融资-----股权融资的四种方式(上)

’在企业融资的多种手段中&#xff0c;股权质押融资、股权交易增值融资、股权增资扩股融资和股权的私募融资&#xff0c;逐渐成为中小企业利用股权实现融资的有效方式。随着市场体系和监管制度的完善&#xff0c;产权市场为投融资者搭建的交易平台日益成熟&#xff0c;越来越多…

ChatGPT 到 Word:使用 Writage 进行复制粘贴魔法

ChatGPT 到 Word&#xff1a;使用 Writage 进行复制粘贴魔法 写在前面Writage的使用 写在前面 随着ChatGPT的日益普及&#xff0c;越来越多的人每天依赖它来完成各种任务。无论是寻找信息、语言翻译、解决数学问题&#xff0c;还是精炼复杂的概念和文本&#xff0c;ChatGPT 都…

React里面table组件的时间轴的位置计算

如上图的时间轴位置计算 计算时间轴位置倒是容易&#xff0c;主要是React里面的antd的table组件怎么监听滚动是个问题 /*** &#xff08;月台/时间&#xff09;为150&#xff0c;时间为100&#xff0c;每个格子为120&#xff0c;120px/30分钟4px/分钟* 00:00分为250px* 00:…

muduo 网络库源码解析和使用

1. base 模块 1.1 API 1.1.1 eventfd int eventfd(unsigned int initval, int flags);&#xff08;1&#xff09;类似信号量&#xff1b;其内部保存了一个 uint64_t 计数器 count&#xff0c;使用 initval 初始化&#xff1b; &#xff08;2&#xff09;read 没有设置 EFD…

Webpack5入门到原理24:优化代码运行性能

优化代码运行性能 Code Split 为什么 打包代码时会将所有 js 文件打包到一个文件中&#xff0c;体积太大了。我们如果只要渲染首页&#xff0c;就应该只加载首页的 js 文件&#xff0c;其他文件不应该加载。 所以我们需要将打包生成的文件进行代码分割&#xff0c;生成多个…

Suricata-7.0 源码分析之流表建立FlowWorker

一、什么是Flow&#xff1f; 二、Flow是怎么建立的&#xff1f; 三、Flow建立的具体过程是什么&#xff1f; 一、什么是Flow&#xff1f;   在Suricata 7.0中&#xff0c;流Flow是指所有相同五元组&#xff08;协议&#xff0c;源IP&#xff0c;目的IP&#xff0c;源端口&am…