async
和
promise
),本节将分享c++标准库中最后一个多线程异步操作库
package_task
的学习笔记。
头文件
声明方式: template< class R, class ...Args > class packaged_task
简介
package_task
标准类模版包装了任何可调用的目标,其中包括函数,std::bind
表达式,lamda
表达式或者其他函数对象。并支持package_task
对象调用者的异步调用。调用之后的返回值或者产生的异常能够被存储在能够被std::future
对象访问的共享状态中。
综上描述,我们很明显能够体会到该模版类提供的功能和promise
类非常接近,但是promise
类没有办法初始化所有可调用的对象,promise类仅提供共享状态的多种访问机制并提供线程之间变量的共享机制。
构造函数
packaged_task() noexcept;
构造无任务且无共享状态的package_task
对象
template
构造拥有共享状态和任务副本的 std::packaged_task 对象,
packaged_task( const packaged_task& ) = delete;
复制构造函数被删除, std::packaged_task 仅可移动
packaged_task( packaged_task&& rhs ) noexcept;
rhs 之前所占有的共享状态和任务构造 std::packaged_task ,令 rhs 留在无共享状态且拥有被移动后的任务的状态
查看如下代码:
#include
#include
#include 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
";//此时已经将package_task调用对象的执行返回值转交给future对象,所以后续//的线程执行由惰性赋值来触发。即当future的对象的共享状态尝试获取线程执行//结果的时候才进行线程执行,并返回结果。类似std::async的policy:std::launch::deferredauto result = fib_task.get_future(); std::thread t(std::move(fib_task), 40);std::cout << "waiting for task to finish...
";std::cout << result.get() << '
';std::cout << "task complete
";t.join();
}
输出如下:
starting task
waiting for task to finish...
fib result 77
77
task complete
析构函数~packaged_task()
抛弃共享状态并销毁存储的任务对象,同 std::promise::~promise ,若在令共享状态就绪前抛弃它,则存储以 std::future_errc::broken_promise 为 error_code 的 std::future_error 异常
赋值运算符
packaged_task& operator=( const packaged_task& ) = delete
复制赋值运算符被删除, std::packaged_task 仅可移动
std::packaged_task
返回与 *this 共享同一共享状态的 future
同promise
类一样,get_future 只能对每个 packaged_task 调用一次
get_future
成员出现异常的情况如下:
std::packaged_task
成员函数
以转发的 args 为参数调用存储的任务。任务返回值或任何抛出的异常被存储于 *this 的共享状态。
仅在当前线程退出,并销毁所有线程局域存储期对象后,才令共享状态就绪
代码如下
#include
#include
#include
#include
#include
#include 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的惰性求值策略,当调用者想要获取调用对象的执行结果时才开始执行调用对象。
package_task
的优势更多是它能够初始化所有的可调用对象,并且支持对该调用对象的异步访问机制。
promise
更多的优势是线程之间的变量的传递,同时返回future类的共享状态。同时它也能够支持多种共享状态的访问机制,惰性求值/立即执行。
所以C++多线程的几个异步操作promise
、async
和package_task
都可以进行线程之间的异步操作,希望以此笔记在今后多线程编程中各持所需进行学习。
经过长期探索,发现一个不需要手动设置线程休眠时间(e.g. std::this_thread::sleep_for(std::chrono::microseconds(1)))的代码: Github: https://github.com/log4cplus/ThreadPool #ifndef THREAD_POOL_H_7e...
nth_element(first,nth,last) first,last 第一个和最后一个迭代器,也可以直接用数组的位置。 nth,要定位的第nn 个元素,能对它进行随机访问. 将第n_thn_th 元素放到它该放的位置上,左边元素都小于它,右边元素都大于它. 测试代码: http://www.cplusplus.com...
c/c++老版本的rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。
定义在
jsoncpp 是一个C++ 语言实现的json库,非常方便得支持C++得各种数据类型到json 以及 json到各种数据类型的转化。 一个json 类型的数据如下: {"code" : 10.01,"files" : "","msg" : "","uploadid" : "UP000000" } 这种数据类型方便我们人阅读以...
问题如下: 已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子 集中的各个元素和为整数target的子集,结果中无重复的子集。 例如: nums[] = [10, 1, 2, 7, 6, 1, 5], target = 8 结果为: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]] 同样之前有...