C++11多线程(4)

线程返回值

在先前的实践中,我们认识到,我们使用thread去获取线程返回值是比较费劲的。
C++11中提供了两个模板future和promise来协助我们获取线程返回值。
在使用它们的时候应该包含头文件thread

异步任务(async)

异步是什么?
“异步与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。” ------《百度百科》

例如一个线程A可能要执行很长时间,而此时另外一个与它无关联的线程B需要执行,它不会去阻塞B,若是A此时阻塞了B,而且B线程对实时性要求高,对用户来说就像是程序假死了一般,这是不合适的。
来看看我们如何创建一个异步线程。

#include <iostream>
#include <thread>
#include <future>		//async所在的头文件

using namespace std;

//线程入口函数
int my_thread(){		
	cout << "the thread_Id is :" << this_thread::get_id() << endl;
	std::chrono::milliseconds delay(5);		//设置五秒的休眠时间
	std::this_thread::sleep_for(delay);		//休眠五秒
	return 1;
}

int main(){
	cout << "the main thread_Id is :" << this_thread::get_id() << endl;
	//创建一个异步任务
	std::future<int> res = async(std::launch::async | std::launch::deferred,my_thread);
	//创建异步任务状态对象,并等待线程执行6秒
	std::future_status status = res.wait_for(std::chrono::second(6));	//约定线程执行的时间
	if(status == std::future::timeout){		//线程执行时间超过我们约定的时间
		cout << "线程执行超时" << endl;
	}else if(status == std::future_status::ready){		//在约定时间内完成
		cout << "线程在指定时间内返回了" << endl;
		cout << res.get() << endl;	//返回值获取
	}else if(status === std::future_status::deferred){	//async选择了延迟执行
		cout << "该线程被延迟执行了" << endl;
		cout << res.get() << endl;	//返回值获取,不写这个,线程根本不会执行
	}
	return 0;
}

执行结果:
执行结果

参数

std::async(),我们使用它来创建一个异步任务,它返回一个future对象(下面会说)。
注:它有时候并不创建一个线程,而是在某个线程调用get()时在该线程中执行(当参数为std::deferred的时候)

  1. std::launch::async。 它会强制创建一个线程(异步任务),并在新线程中执行。
  2. std::launch::deferred。它会将程序延迟执行,而且,如果没有wait和get在后面承接,它将不会执行,大家可以试试。(它不会创建一个新线程)。
  3. std::launch::async | std::launch::deferred。即我们在程序中使用的那个参数,它和不带任何参数效果一样(缺省参数),系统会自行决定,挑选合适的参数。(一般系统资源多的时候,使用 std::launch::async,少则使用std::launch::deferred)。

跟thread的比较

1,std::thread()创建线程,如果创建失败,程序将会抛出异常
2,std::thread()拿到返回值比较费劲

1,std::async()创建线程,如果系统资源紧张,它不会创建新线程,所以不会抛出异常,则异步任务会执行在调用get()的那个线程上
2, std::async()可调用内部函数来取得返回值(下面就会讲)

返回值获取实践

上面我们说了怎么创建一个异步任务。接下来,我们讲如何获取一个线程的返回值。

future

#include <iosteram>
#include <thread>
#include <future>

using namespace std;

int my_thread(int mypar){
	cout << mypar << endl;
	cout << "我的线程开始执行了,iD是:" << std::this_thread::get_id() << endl;
	std::chrono::milliseconds cost(5000);
	std::this_thread::sleep_for(cost);
	cout << "我的线程结束执行了,iD是:" << std::this_thread::get_id() << endl;
	return 5;
}

int main(){
	//创建了一个future对象,并将一个异步任务与它绑定
	std::future<int(int>> m_f = async(my_thread,88);
	cout << m_f.get() << endl;
	return 0;
}

future是一个记录异步事件值及执行状态的对象,状态上面已经演示了,它可以通过get方法获取其内部值(move语义,不能多次调用)。它由async,packaged_task和promise产生。

shared_future

如果想要多次调用该返回值,可以用shared_future,它的get()的语义是复制,所以可以多次调用。

#include <iostream>
#include <thread>
#include <future>

int my_thread(int num){
	int ans = num;
	return ans;
}

int main(){
	std::shared_future<int(int)> m_f_s = async(my_thread,18);
	for(int i = 0;i < 10;++i){
		cout << m_f.get() << endl;
	}
	return 0;
}
成员函数

get() 会阻塞其他线程,等待线程入口函数执行完,并返回结果,才放弃阻塞,它不能多次调用
wait() 会等待异步操作完成,没有返回值
wait_for() 会超时等待返回结果

promise

#include <iostream>
#include <thread>
#include <future>

using namespace std;

int my_thread(std::promise<int> &pro,int calc){
	//假设这五秒我们都在做复杂处理
	std::chrono::milliseconds cost(5000);
	std::this_thread::sleep_for(cost);
	//计算出了结果
	int ans = calc;		//报存结果
	num.set_value(ans);	//保存结果到一个promise对象中
	return ans;
}
int main(){
	promise<int> my_prom;	//声明一个promise对象,保存的类型为int
	thread th(my_thread,std::ref(myprom),88);	//声明一个带promise的线程
	th.join();
	
	//结果获取
	std::future<int> fue = myprom.get_future();		//将myprom和future绑定,用于获取返回值
	int res = fue.get();	//获取值
	cout << "返回值是: " << res << endl;
	return 0;
}

promise是特殊的future,它的对象是异步provider,它可以在某一时刻,设置共享状态的值(set_value())。

成员函数

get_future() 返回一个与promise共享状态的future对象,返回的future对象可以访问由promise对象设置在共享状态上的值或者某个异常对象。
set_value() 设置共享状态的值,此后promise的共享状态标志变为ready
set_exception() 为 promise 设置异常,此后 promise 的共享状态变标志变为 ready。
swap() 交换promise的共享状态

packaged_task

#include <iostream>
#include <thread>
#include <future>

using namespace std;

int my_thread(int num){
	std::chrono::milliseconds delay(5000);
	std::this_thread::sleep_for(delay);
	int ans = num;
	return ans;
}
int main(){
	//通过packaged_task将my_thread包装起来,
	packaged_task<int(int)> my_pt(my_thread);
	thread th(std::ref(my_pt),10);
	th.join();
	//将future和packaged绑定
	future<int> res = my_pt.get_future();
	cout << res.get() << endl;				
	return 0;
}

packaged用来包装一个可调用对象,它的对象是一个异步provider,它在某一时刻通过调用被包装的对象来设置共享状态的值。它的结果需传递给一个future对象去获取返回值。

成员函数

get_future() 返回一个与packaged_task共享状态的future对象,返回的future对象可以获得由另外一个线程在该packaged_task对象的共享状态下设置的值或者异常。
vaild() 检查当前packaged_task是否与一个由小的状态互相关联,对于由默认构造函数生成的packaged_task对象,该函数返回false,除非中间进行了move复制操作或者swap操作。
reset() 重置packaged_task的共享状态,但是保留之前的被包装的任务
swap() 交换packaged_task的共享状态

总结

我们可以通过对future的使用可以比较容易地从线程中获取返回值,而且,在异步任务的状态下,我们可以避免一些由于某线程运行时间过长,导致程序假死(通过对异步任务状态的控制)。

在写这个的时候,参考了大佬的几篇博客:大佬的地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值