文章目录
- 描述
- 函数成员及使用
- 总结
我们上一篇描述关于C++多线程中的异步操作相关库(
async
和
promise
),本节将分享c++标准库中最后一个多线程异步操作库
package_task
的学习笔记。
描述
- 头文件
<future>
- 声明方式:
template< class R, class ...Args > class packaged_task<R(Args...)>;
-
简介
标准类模版包装了任何可调用的目标,其中包括函数,package_task
表达式,std::bind
表达式或者其他函数对象。并支持lamda
对象调用者的异步调用。调用之后的返回值或者产生的异常能够被存储在能够被package_task
std::future
对象访问的共享状态中。
综上描述,我们很明显能够体会到该模版类提供的功能和
类非常接近,但是promise
类没有办法初始化所有可调用的对象,promise类仅提供共享状态的多种访问机制并提供线程之间变量的共享机制。promise
函数成员及使用
-
构造函数
构造无任务且无共享状态的packaged_task() noexcept;
package_task
对象
template <class F> explicit packaged_task( F&& f )
构造拥有共享状态和任务副本的 std::packaged_task 对象,
packaged_task( const packaged_task& ) = delete;
复制构造函数被删除, std::packaged_task 仅可移动
packaged_task( packaged_task&& rhs ) noexcept;
rhs 之前所占有的共享状态和任务构造 std::packaged_task ,令 rhs 留在无共享状态且拥有被移动后的任务的状态
查看如下代码:
#include <future>
#include <iostream>
#include <thread>
int fib(int n)
{
if (n < 3) return 1;
else
{
std::cout << "fib result " << (n-1)+(n-2) << std::endl;
return (n-1) + (n-2);
}
}
int main()
{
std::packaged_task<int(int)> fib_task(&fib);
std::cout << "starting task\n";
//此时已经将package_task调用对象的执行返回值转交给future对象,所以后续
//的线程执行由惰性赋值来触发。即当future的对象的共享状态尝试获取线程执行
//结果的时候才进行线程执行,并返回结果。类似std::async的policy:std::launch::deferred
auto result = fib_task.get_future();
std::thread t(std::move(fib_task), 40);
std::cout << "waiting for task to finish...\n";
std::cout << result.get() << '\n';
std::cout << "task complete\n";
t.join();
}
输出如下:
starting task
waiting for task to finish...
fib result 77
77
task complete
- 析构函数
抛弃共享状态并销毁存储的任务对象,同 std::promise::~promise ,若在令共享状态就绪前抛弃它,则存储以 std::future_errc::broken_promise 为 error_code 的 std::future_error 异常~packaged_task()
-
赋值运算符
复制赋值运算符被删除, std::packaged_task 仅可移动packaged_task& operator=( const packaged_task& ) = delete
-
std::packaged_task<R(Args...)>::get_future
返回与 *this 共享同一共享状态的 future
同
promise
类一样,get_future 只能对每个 packaged_task 调用一次
成员出现异常的情况如下:get_future
- 已通过调用 get_future 取得共享状态。设置 error_category 为 future_already_retrieved
- *this 无共享状态。设置 error_category 为 no_state
-
std::packaged_task<R(Args...)>::make_ready_at_thread_exit
成员函数
以转发的 args 为参数调用存储的任务。任务返回值或任何抛出的异常被存储于 *this 的共享状态。
仅在当前线程退出,并销毁所有线程局域存储期对象后,才令共享状态就绪
代码如下
#include <future>
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <utility>
void worker(std::future<void>& output)
{
std::packaged_task<void(bool&)> my_task{ [](bool& done) { done=true; } };
auto result = my_task.get_future();
bool done = false;
//根据打印已经可以看到,此时已经执行了package_task的线程内容
//但是共享状态到函数作用域结束之前并未就绪
my_task.make_ready_at_thread_exit(done); // 立即执行任务
std::cout << "worker: done = " << std::boolalpha << done << std::endl;
auto status = result.wait_for(std::chrono::seconds(0));
if (status == std::future_status::timeout)
std::cout << "worker: result is not ready yet" << std::endl;
output = std::move(result);
}
int main()
{
std::future<void> result;
std::thread{worker, std::ref(result)}.join();
//等到线程函数返回结果,且future对象就绪之后,可以看到打印状态变为就绪
auto status = result.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready)
std::cout << "main: result is ready" << std::endl;
}
总结
综上对
package_task
的描述,我们可以看到package_task模版类就像
promise
一样可以被异步调用,并且将调用对象执行的结果封装在
future
的共享状态中,来让调用者获取。同时
package_task
的调用者获取调用对象的执行结果过程就像async的惰性求值策略,当调用者想要获取调用对象的执行结果时才开始执行调用对象。