一、std::async、std::future创建后台任务并返回值
希望线程返回一个结果;
std::async
是个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::future对象
“启动一个异步任务”:自动创建一个线程并开始执行对应的线程入口函数,它返回一个std::future对象,
这个future对象里边就有含有线程入口函数锁返回的结果(线程返回的结果),我们可以通过调用future对象的成员函数get()来获取结果;
“future”:将来的意思,有人也称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没有办法马上拿到,在线程执行完毕的时候,你就能够拿到结果了,所以可以这么理解:这个future对象里会保存一个值,在将来的某个时刻能拿到
#include <thread>
#include <chrono>
#include <future>
int mythread()
{
cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
return 5;
}
int main()
{
cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
//自动创建一个线程并开始执行对应的线程入口函数,它返回了一个std::future对象
std::future<int> result = std::async(mythread); //创建一个线程并开始执行,绑定关系;流程并不卡到这里
cout << "continue......!" << endl;
int def;
def = 0;
cout << result.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
cout << "I Love China!" << endl;
return 0;
}
上述程序通过std::future
对象的get()
成员函数等待线程执行结束并返回结果;
这个get()函数很牛,不拿到将来的返回值誓不罢休,不拿到值我就卡在这里就等待拿值
std::future::get()
只能调用一次,否则会报异常
std::future::wait()
int main()
{
cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
//自动创建一个线程并开始执行对应的线程入口函数,它返回了一个std::future对象
std::future<int> result = std::async(mythread); //创建一个线程并开始执行,绑定关系;流程并不卡到这里
cout << "continue......!" << endl;
int def;
def = 0;
// cout << result.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
result.wait(); //等待线程返回,本身并不返回结果
cout << "I Love China!" << endl;
return 0;
}
#include <thread>
#include <chrono>
#include <future>
class A
{
public:
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
return 5;
}
};
int main()
{
A a;
int tmppar = 12;
cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
//自动创建一个线程并开始执行对应的线程入口函数,它返回了一个std::future对象
// std::future<int> result = std::async(mythread); //创建一个线程并开始执行,绑定关系;流程并不卡到这里
std::future<int> result = std::async(&A::mythread,&a,tmppar); //第二个参数是对象引用,才能保证线程里用的是同一个对象
cout << "continue......!" << endl;
int def;
def = 0;
cout << result.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
// result.wait(); //等待线程返回,本身并不返回结果
cout << "I Love China!" << endl;
return 0;
}
通过额外向std::async()
传递一个参数,该参数的类型是std::launch
类型(枚举类型),来达到一些特殊的目的;
a) std::launch::deferred
:
std::future<int> result = std::async(std::launch::deferred,&A::mythread,&a,tmppar);
表示线程入口函数调用被延迟到std::future
的wait
或者get()
函数调用时才执行;
那如果没有调用wait()或get(),那么线程会执行吗?
答:没执行,实际上,线程根本没有别创建。
std::launch::deferred
:延迟调用,并且没有创建新线程,是在主线程中调用的线程入口函数(线程ID都一样);
b) std::launch::async
:
std::future<int> result = std::async(std::launch::async,&A::mythread,&a,tmppar);
在调用async
函数的时候就开始创建线程,
只要用了std::launch::async
,不需要get()
函数,线程就启动起来。
std::launch::async
和 std::launch::deferred
不可混合调用,没有延迟。
二、std::packaged_task:
打包任务,把任务包装起来;是个类模板,它的模板参数是各种可调用对象;通过std::packaged_task来把各种可调用对象包装起来,方便将来作为现场入口参数
可以对lambda表达式进行封装:
#include <thread>
#include <chrono>
#include <future>
int main()
{
cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
std::packaged_task<int(int)> mypt([](int mypar){
cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
return 5;
}); //我们把函数mythread通过packaged_task包装起来
mypt(105); //直接调用,相当于函数调用
std::future<int> result = mypt.get_future();
cout << result.get() << endl;
return 0;
}
也可以使用容器来存放packaged_task对象:
vector<std::packaged_task<int(int)>> mytasks;
int main()
{
cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
std::packaged_task<int(int)> mypt([](int mypar){
cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
return 5;
}); //我们把函数mythread通过packaged_task包装起来
mytasks.push_back(std::move(mypt)); //入容器,这里用了移动语义,入进去之后mypt就为空
std::packaged_task<int(int)> mypt2;
auto iter = mytasks.begin();
mypt2 = std::move(*iter);
mytasks.erase(iter); //删除第一个元素,迭代已经失效了,所以后续代码不可以再使用iter
mypt2(123);
std::future<int> result = mypt2.get_future();
cout << result.get() << endl;
return 0;
}
三、std::promise
类模板,我们能够在某个线程中给它赋值,然后可以在其他线程中,把这个值取出来用
#include <thread>
#include <chrono>
#include <future>
void mythread(std::promise<int> &tmpp,int calc)
{
calc++;
calc *= 10;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
//终于计算出结果了
int result = calc;
tmpp.set_value(result); //结果我保存到了tmpp这个对象中
}
int main()
{
std::promise<int> myprom; //声明一个std::promise对象myprm,保存的值类型为int
std::thread t1(mythread,std::ref(myprom),180);
t1.join();
//获取结果值
std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值
auto result = fu1.get(); //get只能调用一次,不能调用多次
cout << "result = " << result << endl;
cout << "I Love China!" << endl;
return 0;
}
总结:
通过promise
保存一个值,再将来某个时刻通过一个future
绑定到这个promise
上来得到这个绑定的值
注意:
如果用thread对象,一定要用join等,否则程序最终会报异常
#include <thread>
#include <chrono>
#include <future>
void mythread(std::promise<int> &tmpp,int calc)
{
calc++;
calc *= 10;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
//终于计算出结果了
int result = calc;
tmpp.set_value(result); //结果我保存到了tmpp这个对象中
}
void mythread2(std::future<int> &tmpf)
{
auto result = tmpf.get();
cout << "mythread2 result" << result << endl;
return;
}
int main()
{
std::promise<int> myprom; //声明一个std::promise对象myprm,保存的值类型为int
std::thread t1(mythread,std::ref(myprom),180);
// t1.join();
//获取结果值
std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值
std::thread t2(mythread2,std::ref(fu1));
t1.join();
t2.join();
auto result = fu1.get(); //get只能调用一次,不能调用多次
cout << "result = " << result << endl;
cout << "I Love China!" << endl;
return 0;
}
四、小结
到底怎么用,什么时候用
我们学习这些东西的目的,并不是要把他们都用在自己的实际开发中。
相反,如果我们能够用最少的东西能够写出一个稳定的高效的多线程程序,更值得赞赏。