C++11的异步操作(std::future,std::promise,std::packaged_task,std::async )
C+11提供了异步操作相关的类,主要有std::future,std::promise,std::packaged_task。
std::future作为异步结果的传输通道,可以很方便地获取线程函数的返回值;std::promise用来包装一个值,将数据和future绑定起来,方便线程赋值;std::packaged_task用来包装一个可调用对象,将函数和future绑定起来,以便异步调用。
std::future
C++11中增加的线程,使得我们可以非常方便地创建和使用线程,但有时候会有些不便,比如希望获取线程函数的返回结果,就不能直接通过thread.join()得到结果,这时就必须定义一个变量,在线程函数中去给这个变量赋值,然后执行join(),最后得到结果,这个过程是比较繁琐的。thread库提供了future用来访问异步操作的结果,因为一个异步操作的结果不能马上获取,只能在未来某个时候从某个地方获取,这个异步操作的结果是一个未来的期待值,所以被称为future,future提供了获取异步操作结果的通道。我们可以以同步等待的方式来获取结果,可以通过查询future的状态来获取异步操作的结果。future_status有如下3种状态:
Deferred:异步操作还没有开始。
Ready:异步操作已经完成。
TimeOut:异步操作超时。
我们可以查询future的状态,通过它内部的状态可以知道异步任务的执行情况。
#include <iostream>
#include <future>
#include <iostream>
using namespace std;
int mythread(int val) // 线程入口函数
{
cout << "mythread run..." << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
return val;
}
int main()
{
///异步创建一个task
std::future<int> result = std::async(std::launch::async, mythread, 180);
std::future_status status;
do
{
status = result.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred)
{
cout << "deferred..." << endl;
}
else if (status == std::future_status::timeout)
{
cout << "timeout..." << endl;
}
else if (status == std::future_status::ready)
{
cout << "ready..." << endl;
}
}while(status != std::future_status::ready);
cout << "result is " << result.get() << endl;
return 0;
}
获取future结果有3种方式:get,wait,wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值,wait_for是超时等待返回结果。
上面例子程序的返回如下所示:
mythread run...
timeout...
timeout...
timeout...
timeout...
ready...
result is 180
std::promise
std::promise将数据和future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传进来的promise赋值,在线程函数执行完成之后就可以通过promis的future获取该值了。值得注意的是,取值是间接得通过promise内部提供的future来获取的。std::promise的基本用法如下:
#include <iostream>
#include <future>
#include <thread>
#include <utility>
#include <unistd.h>
using namespace std;
void func(int a, std::promise<int>& p)
{
a += 10;
p.set_value_at_thread_exit(a);
}
int main()
{
int a = 20;
std::promise<int> pr;
thread t(func, a, std::ref(pr));
t.join();
std::future<int> f = pr.get_future();
auto r = f.get();
cout << r << endl;
return 0;
}
std::packaged_task
std::packaged_task包装了一个可调用对象的包装类(如function,lambda expression,bind expression和another function object),将函数和future绑定起来,以便异步调用,它和std::promise在某种程度上有点像,promise保存了一个共享状态,而packaged_task保存的是一个函数。std::packaged_task的基本用法如下:
#include <iostream>
#include <future>
#include <thread>
#include <utility>
#include <unistd.h>
using namespace std;
int func()
{
return 8;
}
int main()
{
std::packaged_task<int()> task(func);
std::thread t1(std::ref(task));
t1.join();
std::future<int> f1 = task.get_future();
cout << f1.get() << endl;
return 0;
}
三者的关系
std::future提供了一个访问异步操作结果的机制,它和线程是一个级别的,属于低层次的对象。在std::future之上的高一层是std::packaged_task和std::promise,它们内部都有future以便访问异步操作结果,std::packaged_task包装的是一个异步操作,而std::promise包装的是一个值,都是为了方便异步操作,因为有时需要获取线程中的某个值,这时就用std::promise,而有时需要获一个异步操作的返回值,这时就用std::packaged_task。那么std::promise和std::packaged_task之间又是什么关系呢?可以将一个异步操作的结果保存到std::promise中。
future被promise和packaged_task用来作为异步操作或者异步结果的连接通道,用std::future和std::shared_future来获取异步调用的结果。future是不可拷贝的,只能移动,shared_future是可以拷贝的,当需要将future放到容器中则需要用shared_future。
packaged_task和shared_future的基本用法如下所示:
#include <iostream>
#include <utility>
#include <future>
#include <thread>
#include <vector>
using namespace std;
int func(int x)
{
return x + 2;
}
int add(int x, int y)
{
return x + y;
}
int main()
{
std::packaged_task<int(int)> task(func);
std::future<int> fut = task.get_future(); ///获取future
std::thread t(std::move(task), 2); ///将task作为线程函数
t.detach();
int value = fut.get(); ///等待task完成并获取结果
cout << "The result is " << value << endl;
vector<std::shared_future<int>> v;
///std::future是不能被复制的,不能放到容器中,需要用shared_future
auto f = std::async(std::launch::async, add, 2, 3);
///auto f = std::async(std::launch::async, [](int a, int b){return a + b; }, 2, 3);
///v.push_back(f);
v.push_back(move(f));
cout << "The shared_future result is " << v[0].get() << endl;
return 0;
}
线程异步操作函数async
std::async比std::promise,std:packaged_task和std::thread更高一层,它可以用来直接创建异步的task,异步任务返回的结果也保存在future中,当需要获取异步任务的结果时,只需要调用future.get()方法即可,如果不关注异步任务的结果,只是简单的等待任务完成的话,则调用future.wait()方法。
现在看一下std::async的原型(std::launch::async | std::launch::deferred, f,args...),第一个参数是线程的创建策略,两种策略,默认的策略是立即创建线程。
std::launch::async:在调用async时就开始创建线程。
std::lauch::deferred:延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait时才创建线程。
第二哥参数是线程函数,第三个参数是线程函数的参数。
std::async的基本用法如下所示:
#include <iostream>
#include <utility>
#include <future>
#include <thread>
#include <vector>
using namespace std;
int func1()
{
return 8;
}
int func3()
{
cout << "func3 call....." << endl;
std::this_thread::sleep_for(std::chrono::seconds(8));
return 8;
}
int main()
{
std::future<int> f1 = std::async(std::launch::async, func1);
cout << f1.get() << endl;
std::future<int> f2 = std::async(std::launch::async, func1);
f2.wait();
std::future<int> f3 = std::async(std::launch::async, func3);
std::future_status status;
do
{
status = f3.wait_for(std::chrono::seconds(1));
if (std::future_status::deferred == status)
{
cout << "deferred..." << endl;
}
else if (std::future_status::timeout == status)
{
cout << "timeout..." << endl;
}
else if (std::future_status::ready == status)
{
cout << "ready..." << endl;
}
}while(status != std::future_status::ready);
cout << "result is " << f3.get() << endl;
return 0;
}
可能的结果如下:
8
func3 call.....
timeout...
timeout...
timeout...
timeout...
timeout...
timeout...
timeout...
ready...
result is 8
std::async是更高层次的异步操作,使得我们不用关注线程创建内部细节,就能方便的获取异步执行状态和结果,还可以指定线程创建策略:应该用std::async替代线程的创建,让它成为我们做异步操作的首选。