C++11多线程(六) 异步任务浅析

C++11多线程(六)异步函数浅解

在之前线程的讲解中不知道你们会不会思考到一个问题?

我们创建线程所调用的函数基本都是void并且没有返回值的,那如果我们使用线程来计算结果的时候应该怎么来写?这种时候我们就要引进新的概念,异步函数。

首先我们先来了解一下future。

std::future

std::future是一个模板类,用于保存异步函数返回的值(共享状态)。共享状态你可以理解为是否获得了运行函数的返回值,如果已经获得了代表共享状态就绪,这个future对象有效的,反之则未就绪 。future有默认的构造函数,但创建出来的future是无效的,此外它还支持移动构造函数。

那如何创建一个有效的future对象呢?

利用async对象。

#include<future> //引入头文件

int f(int a,int b){ return a+b;}

future<int> res1 ;//默认构造函数,无效
future<int> res2 = async(f,100,200);//有效,async创建了有效的future

这样res2就是一个有效的future对象。

除此之外我们还有可以使用promise、packaged_task来创建,之后会再解释。

future常用函数

std::future::wait();

wait函数会在阻塞调用wait的线程直到共享状态就绪。

#include<iostream>
#include<future>
using namespace std;
int x = 0;
int f1(int a,int b) {
	chrono::milliseconds duration(5000);
	this_thread::sleep_for(duration);//等待五秒
	return a+b;
}
int f2(int a, int b) {
	return a + b;
}
int main() {
	future<int> result1 = async(f1, 500, 200);
	future<int> result2 = async(f2, 442, 20220);
	result1.wait();
	cout << "x" << endl;
	cout << result1.get() << endl;//后续有讲解
	cout << "m" << endl;
	cout << result2.get() << endl;
}

可以看到我们调用了result1.wait(),而result1调用的函数是f1(其中我们给了5S的等待)。

当我们运行主线程时,x字符总是会5S后在输出,这是因为wait还没有获得来自f1的取值,所以主线程就被wait所阻塞直到f1中的5s结束获得返回值(共享状态转为就绪)以后我们才会继续下一步。

这段代码中还出现了我们接下来要说的get()函数;

std::future::get()

get():顾名思义,取出存在future中的数据,当就绪状态未完成时,get()会阻塞当前线程直到获得future中的值,这点有点像上述wait()函数。

注意这里词汇用的取出而不是获取,当我们调用get()函数获取共享状态中的值以后,这个future对象就会变为无效future,当我们再次调用get()时就会抛出异常(除了std::future::valid() )函数之外,future的成员函数再被无效对象调用后都会抛出异常)。

std::future::valid()

判断future对象是否有效 ,有效返回true,无效返回false。

std::future::wait_for() , std::future::wait_until()

wait_for和wait_until和之前文章所提到的功能类似,不记得的可以往前翻翻。

wait_for():传入一段时间,该函数会阻塞调用线程直到获得共享状态或者超过时间段。

wait_until():传入某个时间点,该函数会阻塞调用线程直到获得共享状态或者超过该时间点。

两个函数的返回值都是枚举类:future_status

future_status有三种类型:

  • ready:共享状态已就绪
  • timeout:超时未就绪
  • deferred:共享状态具有一个延迟函数
std::future::share()

获取future对象的共享状态,返回值是一个shared_future对象。

这个shared_future会获取future的共享状态,因此future将变为无效。

std::shared_future<>

share_future也是一个类模板,和future类似,只不过shared_future可以拷贝并且多个shared_future可以共享某个共享状态的结果。

换个说法,future::get()是转移数据,shared_future::get()则是复制数据。

创建有效future的三个方法

std::async

好了 , 现在对future有了概念,现在我们来了解创建有效future对象的第一个办法——async。

#include<future> //引入头文件

int f(int a,int b){ return a+b;}

future<int> res1 ;//默认构造函数,无效
future<int> res2 = async(f,100,200);//有效,async创建了有效的future
//此处代码和上方一样

async的构造函数和thread是类似的——函数、仿函数、成员方法、匿名函数。(创建线程

不同点在于,async具有启动策略,因此会更加灵活。

启动策略是一个枚举类。

enum class launch {	// names for launch options passed to async
	async = 0x1,
	deferred = 0x2
	}; 

给出源码,可以看到launch的两种策略:

  1. launch::async

    异步启动:

    创建一个新的线程来执行f,f执行完成时,共享状态变为有序。

  2. launch::deferred

    延迟调用:

    当我们调用get()或者wait()之类获取共享状态函数时才会开始执行f。

#include<future> //引入头文件

int f(int a,int b){ return a+b;}

future<int> res1 ;
future<int> res2 = async(f,100,200);//不指定策略,此时策略是由操作系统决定的。
fututr<int> res3 = async(launch::async,f,300,100);//创建时就开始执行
fututr<int> res4 = async(launch::deferred,launch::,f,300,100);//暂不执行

res4.wait();//等待res4的共享状态,此时res4的async才开始执行。

//上面的res2,res3,res4都是通过async创建的有效future
async和thread的区别
  1. thread创建一个线程,async则是声明一个异步任务,async在启动策略是launch::async时是不会创建线程的,但如果是launch::deferred就会创建线程。
  2. thread是有可能创建失败的(资源紧张时),而launch::deferred则是强制创建一个线程。

std::packaged_task

packaged_task用于包装函数(函数,伪函数,lambda表达式等)以方便异步调用。

#include<future>
int f1(int c){
    return c*100;
}
packaged_task<int(int)> my_f_task(f); //将f1包装起来
packaged_task<int(int)> my_lambda_task([](int x){return ++x;});//包装匿名函数

上面我们就将函数包装了起来。

packaged_task::get_future()

packaged_task::get_future()会返回一个与传入packaged_task的函数相关联的future对象。(每个packaged_task::get_future()只能调用一次)

//接上述创建的两个packaged_task
future<int> res1 = my_f_task.get_future();//创建了一个有效的future
packaged_task其余常用函数

packaged_task::valid() :返回该对象是否具有共享状态

packaged_task::make_ready_at_thread_exit() :在线程退出时才使共享状态ready而不是在调用完成后就立即ready

packaged_task::reset() :在保证关联任务不变的前提下重置对象,也就是可以多次调用。

std::promise

promise也是一个类模板。

可以利用其成员函数get_future()获取future对象。

三种方式的区别可以看一下这位博主

C++ 中 async、packaged_task、promise 区别及使用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值