c++线程三

std::async() 、 std::package_task<> 、std::promise<> 、std::future<>、 std::shared_future<>

一、async()可以看做package_task<>和promise<>的封装

他们的作用都是获取线程的返回值。

我们前面所用的线程函数都是void类型的,但其实线程可以有返回值,当然我们可以用全局变量记录线程处理结果。

1、async()函数是最简单的使用方式

int threadFunc(int m)
{
	cout << "threadFunc id:" << this_thread::get_id() << endl;
	this_thread::sleep_for(chrono::seconds(5));
	return ++m;

}
int main()
{
	cout << "main id:" << this_thread::get_id() << endl;
	int i = 1001;
	future<int> result = async(threadFunc, i);
	cout << result.get() << endl;  //get()会等待线程结束并获取返回值,同wait()只等待线程结束,返回类型void

	cout << "程序结束" << endl;

	return 0;
}

async(threadFunc, i);其实是async(std::launch::async | std::launch::deferred, threadFunc, i);的简化形式。

这里引出async和thread的区别:

1、thread是创建线程,当资源紧张无法创建时程序会异常。
2、async是异步任务,当参数是std::launch::async时也会创建进程,但资源紧张系统无法创建线程时则不创建,此时的用法同参数std::launch::deferred。
3、std::launch::deferred延时调用,不创建进程,只是等到调用std::future<>::get()方法时才进行调用,但还是在同一个线程中。
4、std::launch::async | std::launch::deferred 同时使用,是创建线程还是延时调用,取决于系统。所以在我们想创建线程时要明确指定参数std::launch::async.
创建一个异步任务函数,打印其线程id与主线程id相比较,就能看出std::launch::async和std::launch::deferred的区别。

二、promise<T>

示例如下,用可调用对象做promise的类型。


using T = function<int(int, int)>;
int calpuls(int a,int b)
{
	return a + b;
}
void threadfunc1(promise<T> &pm)
{
	cout << "threadfunc1 id:" << this_thread::get_id << endl;
	this_thread::sleep_for(chrono::seconds(3));
	pm.set_value(bind(&calpuls, std::placeholders::_1, std::placeholders::_2));
}
void thread2(future<T> &ft)
{
	cout << "thread2 id:" << this_thread::get_id << endl;
	auto f = ft.get();
	cout << f(5, 8) << endl;
}

int main()
{
	cout << "main id:" << this_thread::get_id() << endl;
	promise<T> pmt;                         //定义promise
	future<T> ftt = pmt.get_future();       //绑定future
	thread t1(threadfunc1, std::ref(pmt));  //一定要用ref
	thread t2(thread2, std::ref(ftt));
	t1.join();
	t2.join();

	cout << "main end!" << endl;
	
	return 0;
}

thread2要等待线程threadfunc1的执行,可见也是线程间同步。

三、packaged_task<T>

上文我们promise的类型是function<>包装的可调用对象,其实packaged_task<>主要的功能就是包装一个可调用对象,跟function<T>功能类似,并获取异步调用的结果。 

即他同function<T>一样都是包装可调用对象,但packaged_task<>还能通过future<>获取异步调用的结果。

using T = packaged_task<int(int)>; //可以考虑下用function<int(int)>情况是怎样的
std::deque<T> task_q;
std::mutex mu;
std::condition_variable cond;
int factorial(int i)
{
	return i*i*i;
}

void thread_1()
{
	T t;
	{
		std::unique_lock<std::mutex> locker(mu);
		cond.wait(locker, []() { return !task_q.empty(); });
		t = std::move(task_q.front());  //只能移动,拷贝构造是被删除的
		task_q.pop_front();
	}
	t(3);  //用function<int(int)>只能这里获取结果
}

int main()
{
	std::thread t1(thread_1);
	T t(factorial);
	future<int> fui = t.get_future();
	{
		std::lock_guard<std::mutex> locker(mu);
		task_q.push_back(std::move(t));
	}
	cond.notify_one();
	t1.join();
	cout << "异步调用结果:" << fui.get() << endl; //用packaged_task<>包装后,主线程能获取到执行结果

	return 0;
}

四、future<>、shared_future<>

future<T>获取结果用get(),不获取结果用wait();但只能get()一次,要多次get()需要用shared_future<>.

//第一个线程进行耗时运算
int threadadd(int a, int b)
{
	this_thread::sleep_for(chrono::seconds(2));
	return a + b;
}
//一下两个线程需要线程1的计算结果
void thread1( shared_future<int>& sf )
{
	cout << "thread1 id=" << this_thread::get_id() << " sf.get()=" << sf.get() << endl;	
}
void thread2(shared_future<int>& sf)
{
	cout << "thread1 id=" << this_thread::get_id() << " sf.get()=" << sf.get() << endl;
}

int main()
{
	packaged_task<int(int,int)> pt(threadadd);
	future<int> fui = pt.get_future();
	shared_future<int> sfu = fui.share(); //通过share()转成shared_future

	thread at(std::move(pt), 6, 7);    //packaged_task包装一个函数指针,目的是能获取函数运行结果
	thread t1(thread1,std::ref(sfu));  //以下两个线程都需要at线程的运行结果
	thread t2(thread2, std::ref(sfu)); //这里的参数一定要是shared_future,因为future<>::get()只能一次(所以是移动)
	at.join();
	t1.join();
	t2.join();

	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值