C++多线程:package_task
std::packaged_task
std::packaged_task
包装一个可调用对象,并允许获取该可调用对象计算的结果,可调用对象内部包含两个基本元素:
1.被包装的任务
任务是一个可调用对象,如函数指针或函数对象,该对象的执行结果会传递给共享状态
2.共享状态
用于保存任务的返回结果,并可通过future对象异步访问共享状态
packaged_task
对象的共享状态的生命周期一直会持续到最后一个与之关联的对象被销毁
示例如下:
#include<iostream>
#include<future>
#include<chrono>
#include<thread>
int countdown(int from, int to){
for(int i=from; i!=to; --i){
std::cout<<i<<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout<<"finished"<<std::endl;
return from-to;
}
int main(){
std::packaged_task<int(int,int)> task(countdown);
std::future<int> ret= task.get_future();
std::thread th(std::move(task), 10,0);
int val= ret.get();
std::cout<<"the countdown lasted for "<<val<<" seconds"<<std::endl;
th.join();
return 0;
}
构造函数
- default
初始化一个空的共享状态packaged_task()noexcept;
- initialization
template<class Fn> explicit packaged_task(Fn&&);
- with allocator
template<class Fn, class Alloc> explicit packaged_task(allocator_arg_t, const Alloc&, Fn&&);
拷贝构造函数
pakcaged_task(const package_task&)=delete;
被禁用,不可拷贝构造
移动构造函数
pakcaged_task(const package_task&&)noexcept;
成员函数
-
std::packaged_task::get_future()
获取与共享状态关联的future对象 -
std::packaged_task::valid()
检查packaged_task对象是否与一个有效的共享状态关联
由默认构造函数生成的packaged_task对象,该函数返回false
,除非中间进行了移动赋值或swap
操作std::future<int> launcher(std::packaged_task<int(int)>& tsk, int arg){ if(tsk.valid()){ std::future<int> ret= tsk.get_future(); std::thread(std::move(tsk),arg).detach(); return ret; }else{ return std::future<int>(); } }
-
std::packaged_task::operator()(Args... args)
重载operator()
运算符,调用packaged_task对象包装的任务(函数指针,函数对象或lambda表达式),传参args
- 若调用成功,则将返回值传入packaged_task对象的共享状态
- 若调用失败,则将抛出的异常传入共享状态
以上两种情况的共享状态标志都会变为ready
,因此其他等待该共享状态的线程可以通过std::future::get()
获取值或异常对象
由于被包装的任务在构造
packaged_task
时指定,因此operator()
的效果由该任务决定- 若被包装的任务指向函数或函数对象
std::packaged_task::operator()
仅将参数传递给该任务 - 若被包装的任务指向类的非静态成员函数
std::packaged_task::operator()
的第一个参数应该指定为成员函数被调用的那个对象,其他参数为成员函数的实参 - 若被包装的任务指向类的非静态成员变量
std::packaged_task::operator()
只允许有一个参数
-
std::packaged_task::make_ready_at_thread_exit()
会调用被包装的任务,并传递参数,类似于operator()
,但make_ready_at_thread_exit()
不会立即设置共享状态标志为ready
,而是在线程退出时设置
该函数已经设置了共享状态的值,若在线程结束之前有其他设置或修改共享状态的操作,则会抛出future_error
异常 -
std::packaged_task::reset()
重置packaged_task的共享状态,但是保留之前包装的任务#include<iostream> #include<utiliy> #include<future> #include<thread> int triple(int x){ return x*3; } int main(){ std::packaged_task<int(int)> tsk(triple); std::future<int> fut= tsk.get_future(); std::thread(std::move(tsk),100).detach(); std::cout<<"the triple of 100 is:"<<fut.get()<<std::endl; // re-use same task object tsk.reset(); fut= tsk.get_future(); std::thread(std::move(tsk),200).detach(); std::cout<<"the triple of 200 is:"<<fut.get()<<std::endl; return 0; }
-
std::packaged_task::swap()
交换packaged_task对象的共享状态