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参数,传递0
	promise<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已经用过了,就要重新弄一个promi6
		promise<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;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值