9.async、future、packaged_task、promise

目录

在这里插入图片描述

一、std::async、std::future创建后台任务并返回值

1.目的 :希望线程返回一个结果;

2.头文件:#include <future>

3.用法 :
1.std::future<int> result = std::async(mythread); //创建一个线程开始执行

2.std::future里面的get()等待线程结束并返回结果,wait等 待线程结束但是不返回结果

4.疑问 :为什么不用thread+join直接代替呢?(第十一章详述)

​ 1.std::thread()如果系统资源紧张可能出现创建线程失败的情况, 如果创建线程>失败那么程序就可能崩溃,而且不容易拿到函数返回值 (不是拿不到)
​ 2.std::async()创建异步任务。可能创建线程也可能不创建线程, 并且容易拿到>线程入口函数的返回值;

①、std::async是个函数模板,用来启动一个异步任务,启动起来一个异步任务之后(什么叫“启动一个异步任务”,就是自动创建一个线程并开始执行对应的线程入口函数),他返回一个std::future对象,这个std::future对象里面就含有线程函数返回的结果,我们可以通过调用std::future对象的成员函数get()来获取结果;它返回一个std::future对象。

②、std::future是个类模板。有人也称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没办法马上拿到,在线程执行完毕的时候,你就能拿到结果了,所以大家就这么理解:future对象里会存放一个值,在将来的某个时刻能够拿到;

③、程序通过std::future对象的get()成员函数等待线程执行结束并返回结果,get()只能调用一次

④、这个get()函数很牛,不拿到将来返回的值,誓不罢休,不拿到值,我就卡在那里等待拿值。

⑤、添加内容:std::share_future

share_future也是个类模板,std::shared_futurestd::future 类似,但是 std::shared_future 可以拷贝、多个 std::shared_future 可以共享某个共享状态的最终结果;即,futureget()函数转移数据,share_futureget()函数复制数据。

代码一:

  • 普通函数:std::future<int> result = std::async(mythread);
#include <iostream>
#include <thread>
#include <mutex>
#include <future>
using namespace std;


int mythread() {  //线程入口函数
	cout << "mythread() start" << "thread = " << std::this_thread::get_id() << endl; //打印新线程id值
	std::chrono::milliseconds dura(5000); //休息5秒
	std::this_thread::sleep_for(dura); //休息一定时长
	cout << "mythread() end" << "thread = " << std::this_thread::get_id() << endl;  //打印新线程id值
	return 5;
}


int main() {

	cout << "main" << "thread = " << std::this_thread::get_id() << endl;
	std::future<int> result = std::async(mythread);   //创建一个线程开始执行,绑定关系,流程并不卡在这里
	cout << "continue..." << endl;
	int def;
	def = 0;
	cout << result.get()<<endl; //卡在这里等待mythread()执行完毕,拿到结果;只能调用一次,调用多次报错
    //result.wait(); // 还可以用result.wait();,等待线程返回,本身并不返回结果
	cout << "I love China!" << endl;
	return 0;
}

在这里插入图片描述

代码二:

  • 对于类:std::future<int> result = std::async(&A::mythread,&a,temppar);
class A {
public:
	int mythread(int mypar) {  //线程入口函数
		cout << mypar << endl;
		cout << "mythread() start" << " threadid = " << std::this_thread::get_id() << endl; //打印新线程id值
		std::chrono::milliseconds dura(5000); //休息5秒
		std::this_thread::sleep_for(dura); //休息一定时长
		cout << "mythread() end" << " threadid = " << std::this_thread::get_id() << endl;  //打印新线程id值
		return 5;
	}
};

int main() {
	A a;
	int temppar = 12;
	cout << "main" << " threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result = std::async(&A::mythread,&a,temppar);   //第二个参数得是对象引用,才能保证线程里是同一个对象

	return 0;
}

代码三:

  • 补充

我们还可以额外地向std::async()传递一个参数,该参数类型是std::lunnch类型(枚举类型),达到一些特殊的目的;

*a)*参数:std::launch::deferred: 表示线程入口函数调用被延迟到std::futurewait()或者get()函数调用时才执行;

那如果wait()或者get()没有没调用,那么线程会执行吗?没执行,线程没有创建!

std::launch::deferred:延迟调用,并且没有创建线程,是在主线程中调用的线程入口函数 ;

*b)*参数:std::launch::async:在调用async函数的时候就开始创建线程;std::async()函数。

用法:std::future<int> result = std::async(std::launch::async, &A::mythread2, &a, tmppar);

#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<list>
#include<mutex>
#include<future>
using namespace std;
 
class A
{
public:
	int mythread2(int mypar) //线程入口函数
	{
		cout << mypar << endl;
		cout << "mythread2 start" << "threadid= " << std::this_thread::get_id() << endl; //打印线程id
 
		std::chrono::milliseconds dura(5000); //定一个5秒的时间
		std::this_thread::sleep_for(dura);  //休息一定时常
 
		cout << "mythread2 end" << "threadid= " << std::this_thread::get_id() << endl; //打印线程id
 
		return 5;
	}
};
 
int mythread() //线程入口函数
{
	cout << "mythread start"<< "threadid= " <<std::this_thread::get_id()<<endl; //打印线程id
 
	std::chrono::milliseconds dura(5000); //定一个5秒的时间
	std::this_thread::sleep_for(dura);  //休息一定时常
 
	cout << "mythread end" << "threadid= " << std::this_thread::get_id() << endl; //打印线程id
 
	return 5;
}
int main()
{
	A a;
 
	int tmppar = 12;
 
	cout <<"main" << "threadid= " << std::this_thread::get_id() << endl;
	//std::future<int> result = std::async(mythread);//创建一个线程并开始执行,绑定关系,流程并不卡在这里
	//std::future<int> result = std::async(std::launch::deferred,&A::mythread2,&a,tmppar); //第二参数是对象引用,才能保证线程里面是同一个对象
	std::future<int> result = std::async(std::launch::async, &A::mythread2, &a, tmppar);
	cout << "continue....." << endl;
 
	int def;
	def = 0;
	cout << result.get()<< endl;//卡在这里等待mythread()执行完毕,拿到结果;只能调用一次;
	//result.wait();//等待线程返回,本身并不返回结果
	cout << "I love China!" << endl;
	return 0;
}

二、std::packaged_task

目的:打包任务,把任务包装起来

std::packaged_task是个模板类,它的模板参数是各种可调用对象;通过std::packaged_task来把各种可调用对象包装起来,方便将来作为线程入口函数来调用。

packaged_task包装起来的可调用对象还可以直接调用,所以从这个角度来讲,packaged_task对象,也是一个可调用对象。

get_future(),mythread返回的结果

为什么用ref看第三章节:

因为子线程是拷贝的类,子线程调用拷贝构造函数构造一个新的对象,如果希望子线程中修改值能影响到主线程,可以用std::ref()

实例代码如下:

① 一般格式

//一般格式
#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<list>
#include<mutex>
#include<future>
 
using namespace std;
 
int mythread(int mypar) //线程入口函数
{
	cout << mypar << endl;
	cout << "mythread2 start" << "threadid= " << std::this_thread::get_id() << endl; //打印线程id
 
	std::chrono::milliseconds dura(5000); //定一个5秒的时间
	std::this_thread::sleep_for(dura);  //休息一定时常
 
	cout << "mythread2 end" << "threadid= " << std::this_thread::get_id() << endl; //打印线程id
 
	return 5;
}
 
int main()
{
	cout << "main" << "threadid= " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)> mypt(mythread);//我们把函数mythread通过packaged_task包装起来
	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;
}

② lamdba表达式作为可调用对象、采用容器

//lamdba表达式作为可调用对象、采用容器
#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<list>
#include<mutex>
#include<future>
 
using namespace std;
 
vector < std::packaged_task<int(int)>> mytasks;//容器
 
int main()
{
	cout << "main" << "threadid= " << std::this_thread::get_id() << endl;
 
	std::packaged_task<int(int)> mypt([](int mypar) {
		cout << mypar << endl;
		cout << "mythread2 start" << "threadid= " << std::this_thread::get_id() << endl; //打印线程id
		std::chrono::milliseconds dura(5000); //定一个5秒的时间
		std::this_thread::sleep_for(dura);  //休息一定时常
		cout << "mythread2 end" << "threadid= " << std::this_thread::get_id() << endl; //打印线程id
		return 5;
	});
 
 
	mytasks.push_back(std::move(mypt)); //容器,这里用了移动语义,进去之后mypt就为空
 
	std::packaged_task<int(int)> mypt2;
	auto iter = mytasks.begin();
	mypt2 = std::move(*iter); //移动语义
	mytasks.erase(iter); //删除一个元素,迭代器已经失效了,所以后续代码不可以再使用iter;
 
	mypt2(105);//直接调用,相当于函数调用
	std::future<int> result = mypt2.get_future();
	cout << result.get() << endl;
 
	return 0;
}

三、std::promise

std::promise 类模板,我们能够在某个线程中给它赋值,然后我们可以在其他线程中把这个值取出来用;

总结:通过promise保存一个值,在将来某时刻我们通过把一个future绑定到这个promise上来得到这个绑定的值。

实例代码如下:

#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<list>
#include<mutex>
#include<future>
 
using namespace std;
 
void mythread(std::promise<int>&tmpp, int calc)  //注意第一个参数
{
	//做一系列复杂的操作
	calc++;
	calc *= 10;
 
	//做其他运算,比如整整花费了5秒钟
	std::chrono::milliseconds dura(5000); //定一个5秒的时间
	std::this_thread::sleep_for(dura);  //休息一定时常
 
	int result = calc; //保存结果
	tmpp.set_value(result);  //结果保存到了tmpp这个对象中
} 
 
void mythread2(std::future<int> &tmpf) 
{
	auto result = tmpf.get();
	cout <<"mythread result = " << result<<endl;
}
 
int main()
{
	std::promise<int> myprom; //声明一个std::promise对象myprom,保存的值类型为int;
	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(fu1));
	t2.join(); //等mythread2执行完毕
 
	cout << "I love China!"  << endl;
 
	return 0;
}

四、小结

学习的这些东西,到底怎么用,什么时候用?

我们学习这些东西的目的,并不是要把它们都用在咱们自己的实际开发中。

相反,如果我们能够用最少的东西能够写出一个稳定、高效的多线程程序,更值得赞赏。

为了成长,必须阅读一些高手写的代码,从而快速实现自己代码的积累;我们的技术就会有一个大幅度的提升;

更愿意将学习这些内容的理由解释为:为我们将来能够读懂高手甚至大师写的代码铺路!
线程返回值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值