std::packaged_task 包装一个可调用的对象,并且允许异步获取该可调用对象产生的结果,从包装可调用对象意义上来讲,std::packaged_task 与 std::function 类似,只不过 std::packaged_task 将其包装的可调用对象的执行结果传递给一个 std::future 对象(该对象通常在另外一个线程中获取 std::packaged_task 任务的执行结果)。

std::packaged_task 对象内部包含了两个最基本元素:

  1. 被包装的任务(stored task),任务(task)是一个可调用的对象,如函数指针、成员函数指针或者函数对象
  2. 共享状态(shared state),用于保存任务的返回值,可以通过 std::future 对象来达到异步访问共享状态的效果

可以通过 std::packged_task::get_future 来获取与共享状态相关联的 std::future 对象。在调用该函数之后,两个对象共享相同的共享状态,具体解释如下:

  • std::packaged_task 对象是异步 Provider,它在某一时刻通过调用被包装的任务来设置共享状态的值。
  • std::future 对象是一个异步返回对象,通过它可以获得共享状态的值,当然在必要的时候需要等待共享状态标志变为 ready.

std::packaged_task 的共享状态的生命周期一直持续到最后一个与之相关联的对象被释放或者销毁为止。下面一个小例子大致讲了 std::packaged_task 的用法:

#include <iostream>     // std::cout
#include <future>       // std::packaged_task, std::future
#include <chrono>       // std::chrono::seconds
#include <thread>       // std::thread, std::this_thread::sleep_for// count down taking a second for each value:
int countdown (int from, int to) {for (int i=from; i!=to; --i) {std::cout << i << '\n';std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout << "Finished!\n";return from - to;
}int main ()
{std::packaged_task<int(int,int)> task(countdown); // 设置 packaged_taskstd::future<int> ret = task.get_future();         // 获得与 packaged_task 共享状态相关联的 future 对象.std::thread th(std::move(task), 10, 0);           // 创建一个新线程完成计数任务.int value = ret.get();                            // 等待任务完成并获取结果.std::cout << "The countdown lasted for " << value << " seconds.\n";th.join();return 0;
}

注:ret.get()获得的值是task绑定的packaged_task函数的返回值

std::packaged_task 构造函数

default packaged_task() noexcept;
initialization template < class Fn > explicit packaged_task (Fn&& fn);
with allocator template < class Fn, class Alloc > explicit packaged_task (allocator_arg_t aa, const Alloc& alloc, Fn&& fn);
copy [deleted] packaged_task (const packaged_task&) = delete;
move packaged_task (packaged_task&& x) noexcept;

std::packaged_task 构造函数共有 5 中形式,不过拷贝构造已经被禁用了。下面简单地介绍一下上述几种构造函数的语义:

  1. 默认构造函数,初始化一个空的共享状态,并且该 packaged_task 对象无包装任务。
  2. 初始化一个共享状态,并且被包装任务由参数 fn 指定。
  3. 带自定义内存分配器的构造函数,与默认构造函数类似,但是使用自定义分配器来分配共享状态。
  4. 拷贝构造函数,被禁用。
  5. 移动构造函数。

下面例子介绍了各类构造函数的用法:

#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::threadint main ()
{std::packaged_task<int(int)> foo; // 默认构造函数.// 使用 lambda 表达式初始化一个 packaged_task 对象.std::packaged_task<int(int)> bar([](int x){return x*2;});foo = std::move(bar); // move-赋值操作,也是 C++11 中的新特性.// 获取与 packaged_task 共享状态相关联的 future 对象.std::future<int> ret = foo.get_future();std::thread(std::move(foo), 10).detach(); // 产生线程,调用被包装的任务.int value = ret.get(); // 等待任务完成并获取结果.std::cout << "The double of 10 is " << value << ".\n";return 0;
}

与 std::promise 类似, std::packaged_task 也禁用了普通的赋值操作运算,只允许 move 赋值运算。

std::packaged_task::valid 介绍

检查当前 packaged_task 是否和一个有效的共享状态相关联,对于由默认构造函数生成的 packaged_task 对象,该函数返回 false,除非中间进行了 move 赋值操作或者 swap 操作。

请看下例:

#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::thread// 在新线程中启动一个 int(int) packaged_task.
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>();
}int main ()
{std::packaged_task<int(int)> tsk([](int x){return x*2;});std::future<int> fut = launcher(tsk,25);std::cout << "The double of 25 is " << fut.get() << ".\n";return 0;
}

std::packaged_task::get_future 介绍

返回一个与 packaged_task 对象共享状态相关的 future 对象。返回的 future 对象可以获得由另外一个线程在该 packaged_task 对象的共享状态上设置的某个值或者异常。

请看例子:

#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::threadint main ()
{std::packaged_task<int(int)> tsk([](int x) { return x * 3; }); // package taskstd::future<int> fut = tsk.get_future();     // 获取 future 对象.std::thread(std::move(tsk), 100).detach();   // 生成新线程并调用packaged_task.int value = fut.get();                       // 等待任务完成, 并获取结果.std::cout << "The triple of 100 is " << value << ".\n";return 0;
}

std::packaged_task::operator()(Args… args) 介绍

调用该 packaged_task 对象所包装的对象(通常为函数指针,函数对象,lambda 表达式等),传入的参数为 args. 调用该函数一般会发生两种情况:

  1. 如果成功调用 packaged_task 所包装的对象,则返回值(如果被包装的对象有返回值的话)被保存在 packaged_task 的共享状态中。
  2. 如果调用 packaged_task 所包装的对象失败,并且抛出了异常,则异常也会被保存在 packaged_task 的共享状态中。

以上两种情况都使共享状态的标志变为 ready,因此其他等待该共享状态的线程可以获取共享状态的值或者异常并继续执行下去。

共享状态的值可以通过在 future 对象(由 get_future获得)上调用 get 来获得。

由于被包装的任务在 packaged_task 构造时指定,因此调用 operator() 的效果由 packaged_task 对象构造时所指定的可调用对象来决定:

  • 如果被包装的任务是函数指针或者函数对象,调用 std::packaged_task::operator() 只是将参数传递给被包装的对象。
  • 如果被包装的任务是指向类的非静态成员函数的指针,那么 std::packaged_task::operator() 的第一个参数应该指定为成员函数被调用的那个对象,剩余的参数作为该成员函数的参数。
  • 如果被包装的任务是指向类的非静态成员变量,那么 std::packaged_task::operator() 只允许单个参数。

std::packaged_task::make_ready_at_thread_exit 介绍

该函数会调用被包装的任务,并向任务传递参数,类似 std::packaged_task 的 operator() 成员函数。但是与 operator() 函数不同的是,make_ready_at_thread_exit 并不会立即设置共享状态的标志为 ready,而是在线程退出时设置共享状态的标志。

如果与该 packaged_task 共享状态相关联的 future 对象在 future::get 处等待,则当前的 future::get 调用会被阻塞,直到线程退出。而一旦线程退出,future::get 调用继续执行,或者抛出异常。

注意,该函数已经设置了 promise 共享状态的值,如果在线程结束之前有其他设置或者修改共享状态的值的操作,则会抛出 future_error( promise_already_satisfied )。

std::packaged_task::reset() 介绍

重置 packaged_task 的共享状态,但是保留之前的被包装的任务。
请看例子,该例子中,packaged_task 被重用了多次:

#include <iostream>
#include <cmath>
#include <thread>
#include <future>int main()
{std::packaged_task<int(int,int)> task([](int a, int b) {return std::pow(a, b);});std::future<int> result = task.get_future();task(2, 9);std::cout << "2^9 = " << result.get() << '\n';task.reset();result = task.get_future();std::thread task_td(std::move(task), 2, 10);task_td.join();std::cout << "2^10 = " << result.get() << '\n';
}

std::packaged_task::swap() 介绍

交换 packaged_task 的共享状态。

C++ 并发指南<future>(2)std::packaged_task相关推荐

  1. C++多线程std::async、std::future、std::packaged_task、std::promise

    std::async std::async用于创建异步任务,实际上就是创建一个线程执行相应任务,默认立即开始执行. std::async就是异步编程的高级封装,std::async的操作,其实相当于封 ...

  2. C++——异步操作(std::future、std::async、std::packaged_task、std::promise)

    文章目录 一.std::future与std::async 1.基本概念 2.代码演示 二.std::future与std::packaged_task 1.基本知识 2.代码演示 三.std::fu ...

  3. C++11 并发指南四(future 详解二 std::packaged_task 介绍)

    上一讲<C++11 并发指南四(<future> 详解一 std::promise 介绍)>主要介绍了 <future> 头文件中的 std::promise 类, ...

  4. C++11 并发指南四(future 详解三 std::future std::shared_future)

    上一讲<C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)>主要介绍了 <future> 头文件中的 std::pack ...

  5. C++11 并发指南四(future 详解一 std::promise 介绍)

    前面两讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三(std::mutex 详解)>分别介绍了 std::thread 和 std::m ...

  6. C++并发编程之std::async(), std::future, std::promise, std::packaged_task

    c++11中增加了线程,使得我们可以非常方便的创建线程,它的基本用法是这样的: void f(int n); std::thread t(f, n + 1); t.join(); 但是线程毕竟是属于比 ...

  7. C++ 并发指南< future >(3)std::future std::shared_future

    std::future介绍 简单地说,std::future 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段. std::future 通常由某个 Provider 创建,你可 ...

  8. 【转】C++11 并发指南五(std::condition_variable 详解)

    http://www.cnblogs.com/haippy/p/3252041.html 前面三讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三 ...

  9. std::packaged_task() ---C++17 并发编程

    std::packaged_task() -C++17 并发编程 std::packaged_task<>连结了future对象与函数(或可调用对象). std::packaged_tas ...

最新文章

  1. 大脑天天超负荷,三分天赋,七分练,世间惊现普通脑修炼秘籍
  2. JavaScript作用域学习笔记
  3. OpenStack Nova Release(Rocky to Train)
  4. 解决cmd命令查看python版本“python不是内部命令或外部命令,也不是可执行程序解决方案”的问题
  5. 更改Eclipse Ctrl+1 的Idea 方式
  6. 【白话机器学习】算法理论+实战之支持向量机(SVM)
  7. Nacos配置管理-微服务配置拉取
  8. linux:你不知道的echo
  9. 【java】窗口中监听器的应用
  10. C 线程同步的四种方式(Windows)
  11. 猴子都能懂的数据库范式详解
  12. 昆仑通态通用版找不到驱动_昆仑通态USB下载问题解决方案
  13. 面试题:Java多线程
  14. Specification使用
  15. AEAI Portal中集成百度地图
  16. java调用阿里云短信服务器-发送短信
  17. 数字图像处理实验之伽马变换
  18. [Unity基础]GL图像库
  19. VBA和Python双语对照,Excel编程学习更简单
  20. 不错的google搜索地址

热门文章

  1. 漂亮的HTML网站赞助打赏单页源码
  2. 数码数字字体_2020年最佳创意数字艺术软件
  3. 如何理解电容器容抗等效
  4. 请查收一封来自程序员的情书
  5. c语言c4700错误,C编译错误,运行错误以及常见问题。
  6. testmeshpro合批_Unity合批原理及失败的原因
  7. 十种常见领带的打法图解
  8. 5.4QBXT 模拟赛 (Rank1 机械键盘 蛤蛤)
  9. 【python】2行代码实现gif动图压缩(pygifsicle)
  10. 助教日志_【沈阳航空航天大学软件工程 1,2 班】团队作业排行