本节讨论c++11中std::packaged_task的特性与使用方法

std::packaged_task<>
std::packaged_task<>
是一个类模板,代表一个异步任务。封装了
1、可调用实体,即函数,lambda函数或函数对象
2、一个共享状态,通过关联的回调来存储返回的值或抛出的异常。

假设我们有一个现有的函数从数据库中提取数据并返回,

//从DB获取数据
std::string gtDataFromDB(std::string token){//Do some stuff to fetch the datastd::string data = "Data fetched from DB by filter :: + token;return data;
}

现在我们想在一个单独的线程中执行该函数,但是我们如果在其他线程结束的情况下在主线程中获取结果或异常返回?
一种方法是更改函数声明并在函数中传递std::promise。在传递线程函数中的std::promise<>对象之前,将相关的std::future<>从中取出并保存在主线程中。现在,在线程函数返回其值之前,应该在传入的std::promise参数中设置它,所以它可以在主线程的相关std::future对象中使用。参考第八节

******************************************

但是,如果我们使用std::packaged_task<>,则可以避免创建std::promise和更改函数代码。

使用packaged_task<>创建异步任务
std::packaged_task<>
可以包装一个标准函数,使其适用于作为异步功能运行。
std::packaged_task<>在一个单独的线程中运行时,它会调用相关的回调函数,并将返回值或异常存储在其内部共享状态中。该值可以通过std :: future <>对象在其他线程或主函数中访问。

从上面提到的函数创建一个packaged_task<>,运行于独立的线程并从其future<>对象获取返回值。

创建std::packaged_task<> 对象

std::packaged_task<>对象是一个类模板,因此我们需要将模板参数传递给packaged_task<>,即可调用函数的类型。

//创建封装了回调函数的packaged_task<>
std::packaged_task<std::string(std::string)> task(getDataFromDB);

获取future对象

//从packaged_task<>获取相关future<>
std::future<std::string> result = task.get_future();

传递packaged_task<>给线程

std::packaged_task<>可移动,但是不可复制,所以我们需要将它传递给线程

//传递packaged_task<>给线程以异步运行
std::thread th(std::move(task), "Arg");

由于packaged_task只可以移动,不可以复制,因此我们在将它移动到线程之前从它那里取出了 std::future<>对象。
线程将会执行这个任务,该任务在内部调用相关的可调用实体,例如我们的函数getDataFromDB()。
当这个函数给出返回值时,std::packaged_task<>将其设置为关联的共享状态,getDataFromDB()返回的结果或异常最终会在关联的未来对象中可用。

//获取packaged_task<>返回的结果,即getDataFromDB()返回的值。
std::string data = result.get();

get()函数将会阻塞调用线程,直到有可调用的实体返回,并且std::packaged_task<>将数据设置为其可共享的状态。

完整的例子如下:

#include <iostream>
#include <thread>
#include <future>
#include <string>//从DB获取数据
std::string getDataFromDB(std::string token) {//获取数据std::string data = "Data fetched from DB by Filter :: " + token;return data;
}int main() {//创建封装回调函数的packaged_task<>std::packaged_task<std::string(std::string)> task(getDataFromDB);//从packaged_task<>获取相关的future<>std::future<std::string> result = task.get_future();//将packaged_task传递给线程以异步运行std::thread th(std::move(task), "Arg");//join线程,阻塞直到线程完成时返回th.join();//获取packaged_task<>的结果,即getDataFromDB()的返回值std::string data = result.get();std::cout << data << std::endl;return 0;
}

输出:

Data fetched from DB by Filter :: Arg

我们可以在同一行,用lambda函数或函数对象创建一个packaged_task

使用lambda函数创建packaged_task

#include <iostream>
#include <thread>
#include <future>
#include <string>int main() {//创建封装了lambda函数的packaged_task<>std::packaged_task<std::string(std::string)> task([](std::string token) {std::string data = "Data From " + token;return data;});//从packaged_task<>获取相关的future<>std::future<std::string> result = task.get_future();//将packaged_task传递给线程以异步运行std::thread th(std::move(task), "Arg");//join线程,阻塞直到线程完成时返回th.join();//获取packaged_task<>的结果,即getDataFromDB()的返回值std::string data = result.get();std::cout << data << std::endl;return 0;
}

输出:

Data fetched from DB by Filter :: Arg

使用函数对象创建packaged_task

#include <iostream>
#include <thread>
#include <future>
#include <string>//从DB获取数据的函数对象
struct DBDataFetcher {std::string operator ()(std::string token) {std::string data = "Data From " + token;return data;}
};int main() {//使用函数对象创建packaged_taskstd::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher()));//从packaged_task<>获取相关的future<>std::future<std::string> result = task.get_future();//将packaged_task传递给线程以异步运行std::thread th(std::move(task), "Arg");//join线程,阻塞直到线程完成时返回th.join();//获取packaged_task<>的结果,即getDataFromDB()的返回值std::string data = result.get();std::cout << data << std::endl;return 0;
}

输出:

Data fetched from DB by Filter :: Arg

c++11多线程编程(十):packaged_task介绍与实例相关推荐

  1. Linux与C++11多线程编程(学习笔记)

    多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...

  2. c++11多线程编程同步——使用条件变量condition variable

    简述 在多线程编程中,当多个线程之间需要进行某些同步机制时,如某个线程的执行需要另一个线程完成后才能进行,可以使用条件变量. c++11提供的 condition_variable 类是一个同步原语, ...

  3. 同步和异步Socket多线程编程基本模型介绍

    多线程:  0.  多线程的概念:  多线程是这样一种机制,它允许在程序中并发执行多个线程,且每个线程彼此间互相独立.  并发的理解:  多个线程的执行是并发的,也就是在逻辑上"同时&quo ...

  4. c++11 多线程编程(一)------初始

    什么是并发 并发在生活中随处可见,边走路边说话,边听歌边写代码.计算机术语中的"并发",指的是在单个系统里同时执行多个独立的活动,而不是顺序的一个接一个的执行.对于单核CPU来说, ...

  5. Linux下多线程编程中信号量介绍及简单使用

    在Linux中有两种方法用于处理线程同步:信号量和互斥量. 线程的信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作.如果一个程序中有多个线程试图改变一个信号量的值,系统将保 ...

  6. Multi_thread--Linux下多线程编程中信号量介绍及简单使用

    在Linux中有两种方法用于处理线程同步:信号量和互斥量. 线程的信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作.如果一个程序中有多个线程试图改变一个信号量的值,系统将保 ...

  7. c++11 多线程编程(六)------条件变量(Condition Variable)

    互斥锁std::mutex是一种最常见的线程间同步的手段,但是在有些情况下不太高效. 假设想实现一个简单的消费者生产者模型,一个线程往队列中放入数据,一个线程往队列中取数据,取数据前需要判断一下队列中 ...

  8. c++11 多线程编程(四)------ 死锁(Dead Lock)

    死锁 如果你将某个mutex上锁了,却一直不释放,另一个线程访问该锁保护的资源的时候,就会发生死锁,这种情况下使用lock_guard可以保证析构的时候能够释放锁,然而,当一个操作需要使用两个互斥元的 ...

  9. c++11 多线程编程(三)------ 竞争和互斥锁

    竞争条件 并发代码中最常见的错误之一就是竞争条件(race condition).而其中最常见的就是数据竞争(data race),从整体上来看,所有线程之间共享数据的问题,都是修改数据导致的,如果所 ...

最新文章

  1. linux 光驱挂载
  2. 使用python爬取视频
  3. Sparse low rank approximation
  4. R语言绘制Bump Chart
  5. python __call__的作用,是可以将对象作为方法使用的关键 分析nn.Module源码
  6. math.hypot java_Java math
  7. vue中引入swiper
  8. 十五步骤手把手学会制作网吧XP母盘制作
  9. 华为鸿蒙osbeta发布会,华为鸿蒙 OS Beta 3
  10. 【Python爬虫学习笔记3】requests库
  11. Ajax Control Toolkit 控件介绍
  12. 如何实现Windows Network所有会话的限制登录和访问控制
  13. 用C#将PDF转换为Doc
  14. 代码记录:易语言中精易模块的json文本数据生成
  15. 网络攻防之wireshark抓取登录信息
  16. SSL连接是什么意思
  17. Dreamweaver CS6破解教程[序列号+破解补丁
  18. Spring Boot—Controller 注解
  19. php教程7天速成,练字速成法一周写出一手漂亮的字!【7天练字速成】
  20. 微信小程序小Demo

热门文章

  1. CSS3 使用@font-face引入字体的兼容性方案及优化
  2. chrome浏览器去除蓝色边框和黄色背景色
  3. 数据压缩实验5-JEPG解码
  4. Unity快速入门之四 - Unity模型动画相关
  5. 人脸检测论文:FaceBoxes: A CPU Real-time Face Detector with High Accuracy及其Pytorch实现
  6. Kaggle Top1% 是如何炼成的!
  7. App开发者如何选择移动广告平台1 - 开发者规模分析
  8. 手势识别整体设计流程方案
  9. 【译文】学习深度学习的四个步骤
  10. 急!灾区的食物依然短缺!(找不到原题出处只能这样了.....)