async,future,packaged_task,promise
一.std::async,std::future创建后台任务并返回值
原来通过thread对象创建一个线程,用成员函数join()等待程序完成,现在希望线程返回一个结果,可以把线程的结果赋给一个变量,有一个更好的办法:std::async。
相关概念
std::async是一个类模板,用来启动一个异步任务之后它返回一个std::future的对象,std::future是一个类模板。
- 启动异步任务:自动创建一个线程并开始执行对应的线程入口函数,返回一个std::future的对象。
- 这个std:future的对象里面就有线程入口函数所返回的结果。可以通过调用future对象成员函数get()来获取结果。一个future对象只能调用一次get()。
- future的另外一个成员函数wait()会等待线程返回但本身不返回结果。
语法
1.普通函数
std::future<数据类型> result(future对象名,这里假设为result)=std::async(入口函数名); std::cout<<result.get()<<std::endl; //输出入口函数返回的结果,线程会卡在get()这里等待入口函数执行完毕拿到返回值 //result.wait(); //等待线程返回,本身不返回结果 //不用get()或者wait()不报错但仍然会等待线程执行完毕2.成员函数
std::future<数据类型> result=std::async(&类名,&类对象名,传入参数); std::cout<<result.get()<<std::endl;3.通过额外向std::async()传递一个参数,该参数类型是std::lunnch类型(枚举类型),来达到一些特殊的目的;
- std::launch::deferred
表示线程入口函数调用被延迟到std::future的wait或者get函数调用时才执行,不会创建新线程而是在主线程中调用入口函数std::future<数据类型> result=std::async(launch::deferred,入口函数名); std::cout<<result.get()<<std::endl; - std::launch::async(默认标记)
在调用async函数的时候就开始创建线程
示例
#include<iostream> #include<thread> #include<list> #include<mutex> #include<future> #include<map> using namespace std; class A { public: int print(int val) { cout << "线程print开始,id= " << this_thread::get_id() << endl; this_thread::sleep_for(chrono::seconds(1)); //休息5秒 cout << "线程print结束,id= " << this_thread::get_id() << endl; return val; //返回值 } }; int print1() { cout << "线程print1开始,id= " << this_thread::get_id() << endl; this_thread::sleep_for(chrono::seconds(1)); //休息5秒 cout << "线程print1结束,id= " << this_thread::get_id() << endl; return 5; //返回值 } int main() { cout << "主线程开始,id= " << this_thread::get_id() << endl; future<int> result = async(print1); cout << result.get() << endl; //卡在这里等待print函数执行完拿到返回值 A a; int val = 10; future<int> result2 = async(&A::print,&a,val); cout << result2.get() << endl; std::cout << "主线程结束" << std::endl; return 0; }二.std::packaged_task
是一个类模板,模板参数是各种可调用对象。
作用
通过std::packaged_task把各种可调用对象包装起来,方便作为线程入口函数调用。
语法
std::packaged_task<函数类型(传入函数的参数类型)> 对象名1(入口函数); std::thread 对象名2(std::ref(对象名1),传入的参数); 对象名2.join(); std::future<数据类型>result=对象名1.get_future(); std::cout<<result.get()<<std::endl; //成员函数get_future()保存的是被包装的入口函数返回的值 //或(示例lambda表达式) std::packaged_task<int(int)> p([](int val{函数定义})); int a; p(a); //相当于函数调用没有创建线程 std::future<int>result=p.get_future(); std::cout<<result.get()<<std::endl; //示例:存入容器和取用 vector<std::packaged_task<<int(int)>> v; //容器 v.push_back(std::move(p)); //移动语义,移动后p为空 auto it=v.begin(); packaged_task<int(int)> p2; //接收容器中的对象 p2=std::move(*it);std::packaged_task是一个仅移动类型(move-only),它没有拷贝构造函数,只有移动构造函数。
如果不用std::ref,编译器会尝试拷贝p,导致编译错误;
#include<iostream> #include<thread> #include<list> #include<mutex> #include<future> #include<map> #include<vector> using namespace std; int print1(int val) { cout << "线程print1开始,id= " << this_thread::get_id() << endl; this_thread::sleep_for(chrono::seconds(1)); //休息5秒 cout << "线程print1结束,id= " << this_thread::get_id() << endl; return val; //返回值 } int main() { cout << "主线程开始,id= " << this_thread::get_id() << endl; packaged_task<int(int)> p(print1); thread t1(ref(p), 1); t1.join(); future<int>result = p.get_future(); //成员函数get_future()保存的是p里面的被包装的入口函数返回的值 cout << result.get() << endl; packaged_task<int(int)> p1([](int val) { cout << "线程print1开始,id= " << this_thread::get_id() << endl; this_thread::sleep_for(chrono::seconds(1)); //休息5秒 cout << "线程print1结束,id= " << this_thread::get_id() << endl; return val; //返回值 }); p1(6); vector<packaged_task < int(int) >> v; //容器 v.push_back(std::move(p1)); //移动语义,移动后p1为空 auto it = v.begin(); packaged_task<int(int)> p2; //接收容器中的对象 p2 = std::move(*it); v.erase(it); future<int>result1 = p2.get_future(); cout << result1.get() << endl; std::cout << "主线程结束" << std::endl; return 0; }三.std::promise
作用
在某个线程中给它赋值,然后可以再其他线程中,把这个值取出来用:
语法
//函数示例 void cal(std::promise<int>&temp,int a) { a*=100; temp.set_value(a); //将计算结果存入temp对象 } void thread1(std::future<int>& temp) { auto result = temp.get(); std::cout << result << std::endl; } int main() { std::promise<int> mp; //声明一个promise对象 thread t(cal,std::ref(mp),10); t.join(); std::future<int>f=mp.get_future(); thread t2(thread1, std::ref(f)); //将结果传入t2 t2.join(); cout<<"主线程结束"<<std::endl; return 0; }