17.9 async、future、packaged_task与promise

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

希望线程返回一个结果;
std::async是个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::future对象,std::future是个类模板。
什么叫“启动一个异步任务”,就是自动创建一个线程并开始执行对应的线程入口函数,返回一个std::future对象。
这个std::future对象里边就含有线程入口函数所返回的结果(线程返回的结果);可以通过调用future对象的成员函数get()来获取结果。
“future”:将来的意思,也称呼std::future提供了一种访问异步操作结果的机制,就是说,这个结果没有办法马上拿到,在线程执行完毕的时候,就能够拿到结果了。
future的终极理解:这个future(对象)里会保存一个值,在将来的某个时刻能够拿到。

下列程序通过std::future对象的get()成员函数等待线程执行结束并返回结果。
这个get()函数很牛,不拿到将来的返回值誓不罢休,不拿到值就等待,直到拿到值为止。

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()
{
	cout << "main threadId = " << std::this_thread::get_id() << endl;
	std::future result = std::async(mythread);  //创建一个线程开始执行,绑定关系;流程并不卡到这里
	cout << "contine....!" << endl;
	int def;
	def = 0;
	cout << "def = 0" << endl;
	cout << result.get() << endl;  //卡在这里等待mythread()执行完毕,拿到结果。
	//cout << result.get() << endl;  //get()只能调用一次
	//result.wait();  //等待线程返回,本身不返回结果。有点类似.join();
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

通过额外向std::async()传递一个参数,该参数类型是std::launch类型(枚举类型);来表达一些特殊的目的;
1>std::launch::deferred:表示线程入口函数调用被延迟到std::future的wait()或者get()函数调用时才执行。
如果wait或者get()没有被调用,那么线程会执行吗?
实际上线程根本就没有创建,更不会执行;
std::launch::deferred:延迟调用,并且没有创建新线程,是在主线程中调用的线程入口函数;
2>std::launch::async,在调用async函数的时候就开始创建线程。

async()函数,默认使用的就是std::launch::async

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 temper = 12;
	cout << "main threadId = " << std::this_thread::get_id() << endl;
	//std::future result = std::async(mythread, temper);  //创建一个线程开始执行,绑定关系;流程并不卡到这里
	std::futureresult = std::async(std::launch::async, &A::mythread, &a, temper);  //第二个参数是对象引用,才能保证线程里面用的是同一个对象。
	cout << "contine....!" << endl;
	int def;
	def = 0;
	cout << "def = 0" << endl;
	cout << result.get() << endl;  //卡在这里等待mythread()执行完毕,拿到结果。
	//cout << result.get() << endl;  //get()只能调用一次
	//result.wait();  //等待线程返回,本身不返回结果。有点类似.join();
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

二:std::packaged_task

打包任务,把任务包装起来。是个类模板,它的模板参数是各种可调用对象;通过 packaged_task 来把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
packaged_task 包装起来的可调用对象还可以直接调用,所以,从这个角度来讲,packaged_task对象,也是一个可调用对象。

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()
{
	cout << "main threadId = " << std::this_thread::get_id() << endl;
	std::packaged_task mypt(mythread);  //把函数mythread通过packaged_task包装起来
	//packaged_task  第一个int是函数返回类型,第二个是参数类型
	std::thread t1(std::ref(mypt), 3);  //线程直接开始执行,第二个参数作为线程入口函数的参数。
	t1.join();  //等待线程执行完毕。
	std::future result = mypt.get_future();
	//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
	cout << result.get() << endl;
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

另一种写法

int main()
{
	cout << "main threadId = " << std::this_thread::get_id() << endl;
	std::packaged_task mypt([](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;
		});
	std::thread t1(std::ref(mypt), 3);  //线程直接开始执行,第二个参数作为线程入口函数的参数。
	t1.join();  //等待线程执行完毕。
	std::future result = mypt.get_future();
	//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
	cout << result.get() << endl;
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

另另一种写法,packaged_task包装起来的可调用对象还可以直接调用,packaged_task对象是一个可调用对象,没有创建新的线程。

int main()
{
	cout << "main threadId = " << std::this_thread::get_id() << endl;
	std::packaged_task mypt([](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;
		});
	mypt(105);  //直接调用,相当于函数调用,没有创建新的线程
	std::future result = mypt.get_future();
	cout << result.get() << endl;
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

另另另一种写法

vector > mytasks;  //容器

int main()
{
	cout << "main threadId = " << std::this_thread::get_id() << endl;
	std::packaged_task mypt([](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;
		});

	mytasks.push_back(std::move(mypt));  //入容器,这里用了移动语义,入进去之后mypt就为空。
	std::packaged_task mypt2;  //用来接收mypt
	auto iter = mytasks.begin();
	mypt2 = std::move(*iter);  //移动语义
	mytasks.erase(iter);  //删除第一个元素,迭代器已经失效了,所以后续代码不可以再使用iter。
	mypt2(123);
	std::future result = mypt2.get_future();
	cout << result.get() << endl;
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

三:std::promise

类模板。能够在某个线程中给它赋值,然后可以在其他线程中,把这个值取出来使用;
总结:通过promise保存一个值,在将来某个时刻我们通过把一个future绑定到这个promise上来得到这个绑定的值。
std::promise myprom; //声明一个std::promise对象myporm,保存的值类型为int;

你可能感兴趣的:(c++学习,c++)