c++11多线程中的std::async异步函数

目录

写在前面

解析

future类成员函数

wait_for以及wait_until返回值类型

get函数

valid函数

wait_for函数

std::launch类型

参考博客


写在前面

如果有一个需要长时间运算的线程,需要计算出最终的有效值,但是现在不迫切需要这个数值,如果我们使用std::thread的话,着一定是不可行的,因为std::thread不可以接收返回值,这里需要使用std:async函数模板

我们可以使用std::async创建一个异步任务,返回std:future对象,这个std:future对象持有线程返回的数据

解析

std::saync是一个类模板,他的构造函数包含两种形式:

template <class Fn, class... Args>
  future<typename result_of<Fn(Args...)>::type>
    async (Fn&& fn, Args&&... args);
template <class Fn, class... Args>
  future<typename result_of<Fn(Args...)>::type>
    async (launch policy, Fn&& fn, Args&&... args);

可以知道,std::async的使用方法和std::thread类似,但是在第二个构造函数中,存在一个std::launch类型的参数,后面详细说明此参数,并且std::async都会返回一个std::future对象,用来存放返回的数据。

#include <iostream>
#include <future>
#include <string>
#include <vector>

int mythread()
{
	std::cout << "mythread() start thread id =" << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(5));
	std::cout << "mythread() start thread id =" << std::this_thread::get_id() << std::endl;
	return 5;
}
int main()
{
	std::cout << "main function id:" << std::this_thread::get_id() << std::endl;
	std::future<int> result = std::async(mythread);
	//或者使用auto自动判断返回值类型:auto result = std::async(mythread);

	std::cout << "continue......!" << std::endl;
	std::cout << result.get() << std::endl;
	
	std::cout << "main function finish!" << std::endl;
	return 0;
}

可以看到future变量可以接受线程返回的数值,并且使用get()函数得到返回的数值。

future对象可以有被以下三种方式创建

async

promise::get_future
packaged_task::get_future

future类成员函数

share 返回一个std::shared_future对象,调用函数后future对象不和任何共享状态关联,此时future状态不再是valid(有效的)
get 当与该 std::future 对象相关联的共享状态标志变为 ready 后,调用该函数将返回保存在共享状态中的值,如果共享状态的标志不为 ready,则调用该函数会阻塞当前的调用者,而此后一旦共享状态的标志变为 readyget 返回 Provider 所设置的共享状态的值或者异常(如果抛出了异常)。
valid 检查当前future对象是否有效,通过上述三种方式创建的对象是vaild,还可以使用move将其变为valid
wait

等待与当前std::future 对象相关联的共享状态的标志变为 ready.

如果共享状态的标志不是 ready,调用该函数会被阻塞当前线程,直到共享状态的标志变为 ready

一旦共享状态的标志变为 ready,wait() 函数返回,当前线程被解除阻塞

wait_for 和wait函数类似,需要设置一个时间段rel_time,如果共享状态的标志在时间段结束之前没有被Provider设置为valid,则调用wait_for的线程被堵塞,在等待了rel_time时间后,wait_for函数返回
wait_until 和wait函数类似,需要设置一个系统绝对时间点abs_time,如果共享状态的标志在该时间点到来之前没有被 Provider 设置为 ready,则调用 wait_until 的线程被阻塞,在 abs_time 这一时刻到来之后 wait_until() 返回

wait_for以及wait_until返回值类型

返回类型为future_status,表示导致函数返回的原因:

future_status::ready 共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常
future_status::timemout 超时,即在规定的时间内共享状态的标志没有变为 ready。
future_status::deferred 共享状态包含一个 deferred 函数。如在saync中第一个参数指定为std::launch::deferred

get函数

get函数的源码为:

void get()
		{	// block until ready then return or
			// throw the stored exception
		future _Local{_STD move(*this)};
		_Local._Get_value();
		}

可以看到在get函数中新建了一个_Local的新对象,使用移动语义(一般变量或者函数前面以'_'开头,表示为内部使用的对象或者函数),然后得到_Local的value,这样的话,future对象就会变为无效的,即调用vaild函数会返回false ,因为这里使用移动语义,新对象_Local对象在get函数中被销毁,也就是说future对象只可以调用一次get函数,如果要调用多次get函数,可以使用shared_future对象(使用复制语义)

valid函数

#include <iostream>
#include <future>
#include <string>
#include <vector>

int get_value()
{
	return 10;
}
int main()
{
	std::future<int> foo, bar;
	foo = std::async(get_value);
	bar = std::move(foo);        //使用移动构造函数,foo变为无效,bar变为有效

	if (foo.valid())
		std::cout << "foo's value:" << foo.get() << std::endl;
	else
		std::cout << "foo is not valid\n";

	if (bar.valid())
		std::cout << "bar's value:" << bar.get() << std::endl;
	else
		std::cout << "bar is not valid\n";
	return 0;
}
//栗子来源:http://www.cplusplus.com/reference/future/future/valid/

wait_for函数

#include <iostream>
#include <future>
#include <string>
#include <vector>
bool is_prime(int x) {
	for (int i = 2; i<x; ++i) if (x%i == 0) return false;
	return true;
}

int main()
{
	// call function asynchronously:
	std::future<bool> fut = std::async(is_prime, 700020007);

	// do something while waiting for function to set future:
	std::cout << "checking, please wait";
	std::chrono::milliseconds span(100);
	while (fut.wait_for(span) == std::future_status::timeout)
		std::cout << '.';

	bool x = fut.get();

	std::cout << "\n700020007 " << (x ? "is" : "is not") << " prime.\n";

	return 0;
}
//栗子来源:http://www.cplusplus.com/reference/future/future/wait_for/

std::launch类型

std::launch类型在async的第二种构造函数中看到,一种枚举类型,原型为:

		// ENUM launch
enum class launch {	// names for launch options passed to async
	async = 0x1,
	deferred = 0x2
	};
类型 作用
std::launch::async 

 异步任务会在另外一个线程中调用,并通过共享状态返回异步任务的结果(一般是调用 std::future::get() 获取异步任务的结果),简单说为:

在调用async函数的时候就开始创建线程,并在新线程运行

std::launch::deferred 

异步任务将会在共享状态被访问时调用,相当与按需调用(即延迟(deferred)调用),简单说为:

线程入口函数调用被延迟到std::future的wait或者get函数调用时才执行,如果后面不调用wait或者get函数,就不会执行saync中的函数

std::launch::deferred  |  std::launch::async  默认情况下使用此种参数,表示编译器会根据计算机现在的情况自行选择对应的执行方式
#include <iostream>
#include <future>
#include <string>
#include <vector>

int mythread()
{
	std::cout << "mythread() start thread id =" << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(5));
	std::cout << "mythread() start thread id =" << std::this_thread::get_id() << std::endl;
	return 5;
}
int main()
{
	std::cout << "main function id:" << std::this_thread::get_id() << std::endl;
	std::future<int> result = std::async(std::launch::deferred,mythread);
	//std::future<int> result = std::async(std::launch::async, mythread);

	std::cout << "continue......!" << std::endl;
	std::cout << "main function finish!" << std::endl;
	std::cout << result.get() << std::endl;
	
	return 0;
}

std::launch::deferred 情况下可以发现,并没有创建新线程,而是调用get函数的时候,在主线程中执行对应的函数

std::launch:: async :及正常调用情况,创建新线程执行

#include <iostream>
#include <future>
#include <string>
#include <functional>
#include <algorithm>
#include <vector>

int mythread()
{
	std::cout << "mythread() start thread id =" << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(5));
	std::cout << "mythread() start thread id =" << std::this_thread::get_id() << std::endl;
	return 5;
}

int main()
{
	std::future<int> f = std::async(std::launch::deferred, mythread);
	std::future_status a1 = f.wait_for(std::chrono::seconds(0));
	if (a1 == std::future_status::deferred)
	{
		std::cout << "线程正延迟执行,等待get或者wait函数调用\n";
		f.get();
	}
	else
	{
		if (a1 == std::future_status::ready)
		{
			std::cout << "线程执行完毕\n";
			f.get();
		}
		else
		{
			std::cout << "线程未执行完毕\n";
			f.get();
		}
	}
	return 0;
}

 

参考博客

C++11 并发指南四(<future> 详解三 std::future & std::shared_future) 

http://www.cplusplus.com/reference/future/future/

《C++11并发与多线程视频课程》 视频地址:http://edu.51cto.com/course/15287.html

猜你喜欢

转载自blog.csdn.net/li1615882553/article/details/86252813