[编程基础] C++多线程入门10-packaged_task示例
原始C++标准仅支持单线程编程。新的C++标准(称为C++11或C++0x)于2011年发布。在C++11中,引入了新的线程库。因此运行本文程序需要C++至少符合C++11标准。
10 packaged_task<>示例
在此示例中,我们将讨论c++11中std::packaged_task功能及其用法。std::packaged_task<>是一个类模板,代表一个异步任务。它封装了:
- 可调用实体,即函数,lambda函数或函数对象。
- 一种共享状态,用于存储由关联的回调返回或引发的异常的值。
需要std::packaged_task<>的情况
假设我们有一个现有函数,该函数从数据库中获取数据并返回
// Fetch some data from DB
std::string getDataFromDB(std::string token)
{
// Do some stuff to fetch the data
std::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 <>和更改功能代码。
10.1 结合使用packaged_task <>和函数来创建异步任务
std::packaged_task <>可以包装普通函数,并使其可作为异步函数运行。在单独的线程中调用std:: packaged_task <>时,它将调用关联的回调并将返回值/异常存储在其内部共享状态中。可以通过std:: future <>对象在其他线程或主函数中访问此值。让我们从上述函数创建一个std::packaged_task <>,在单独的线程中执行,并从其future <>对象获取结果。
创建std::packaged_task <>对象
std::package_task <>是类模板,因此我们需要将模板参数传递给packaged_task <>,即可调用函数的类型
// Create a packaged_task<> that encapsulated the callback i.e. a function
std::packaged_task<std::string (std::string)> task(getDataFromDB);
从中获取future对象
// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();
将packaged_task <>传递给线程
std::packaged_task <>是可移动的,但不可复制,因此我们需要将其移动到线程,即
// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");
由于packaged_task仅可移动且不可复制,因此我们在将其移至线程之前从其获取了std::future <>对象。线程将执行此任务,该任务在内部调用关联的可调用实体,即我们的函数getDataFromDB()。
现在,当此函数返回值时,std::packaged_task <>会将其设置为关联的共享状态,并且getDataFromDB()返回的结果或异常最终将在关联的future对象中可用。
在主函数中,从future <>对象获取结果,即
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data = result.get();
get()函数将阻塞调用线程,直到可调用实体返回并且std::packaged_task <>将数据设置为可共享状态为止。完整的示例代码如下:
#include <iostream>
#include <thread>
#include <future>
#include <string>
// Fetch some data from DB
std::string getDataFromDB(std::string token)
{
// Do some stuff to fetch the data
std::string data = "Data fetched from DB by Filter::" + token;
return data;
}
int main()
{
// Create a packaged_task<> that encapsulated the callback i.e. a function
// 创建封装回调函数的packaged_task<>
std::packaged_task<std::string(std::string)> task(getDataFromDB);
// Fetch the associated future<> from packaged_task<>
// 从packaged_task<>中获取关联的future<>对象
std::future<std::string> result = task.get_future();
// Pass the packaged_task to thread to run asynchronously
// 将packaged_task传递给线程以异步运行
std::thread th(std::move(task), "Arg");
// Join the thread. Its blocking and returns when thread is finished.
// 加入线程,完成后返回
th.join();
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
// 获取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()
{
// Create a packaged_task<> that encapsulated a lambda function
std::packaged_task<std::string(std::string)> task([](std::string token) {
// Do some stuff to fetch the data
std::string data = "Data From " + token;
return data;
});
// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();
// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");
// Join the thread. Its blocking and returns when thread is finished.
th.join();
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data = result.get();
std::cout << data << std::endl;
return 0;
}
输出为:
Data From Arg
使用函数对象创建packaged_task
#include <iostream>
#include <thread>
#include <future>
#include <string>
/*
* Function Object to Fetch Data from DB
*/
struct DBDataFetcher
{
std::string operator()(std::string token)
{
// Do some stuff to fetch the data
std::string data = "Data From " + token;
return data;
}
};
int main()
{
// Create a packaged_task<> that encapsulated a lambda function
std::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher()));
// Fetch the associated future<> from packaged_task<>
std::future<std::string> result = task.get_future();
// Pass the packaged_task to thread to run asynchronously
std::thread th(std::move(task), "Arg");
// Join the thread. Its blocking and returns when thread is finished.
th.join();
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()
std::string data = result.get();
std::cout << data << std::endl;
return 0;
}
输出为:
Data From Arg
10.2 参考
https://thispointer.com/c11-multithreading-part-10-packaged_task-example-and-tutorial/
[编程基础] C++多线程入门10-packaged_task示例的更多相关文章
- [编程基础] C++多线程入门9-async教程和示例
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 9 asy ...
- [编程基础] C++多线程入门8-从线程返回值
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 8 从线程返回值 8 ...
- [编程基础] C++多线程入门4-数据共享和资源竞争
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++ 11标准. 4 数据共享和资源 ...
- [编程基础] C++多线程入门7-条件变量介绍
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 7 条件变 ...
- [编程基础] C++多线程入门5-使用互斥锁解决资源竞争
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 5 使用互 ...
- [编程基础] C++多线程入门1-创建线程的三种不同方式
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 1 创建线程的三种不 ...
- [编程基础] C++多线程入门6-事件处理的需求
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 6 事件处 ...
- [编程基础] C++多线程入门3-小心地将参数传递给线程
原始C++标准仅支持单线程编程.新的C++标准(称为c++11或c++0x)于2011年发布.在c++11中,引入了新的线程库.因此运行本文程序需要C++至少符合c++11标准. 文章目录 3 小心地 ...
- [编程基础] C++多线程入门2-连接和分离线程
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 2 连接和 ...
随机推荐
- OnionArch - 如何实现更新指定字段的通用Handler
博主最近失业在家,找工作之余,自己动手写了个洋葱架构(整洁架构)解决方案,以总结和整理以前的项目经验,起名叫OnionArch,其目的是为了更好的实现采用DDD(领域驱动分析)和命令查询职责分离(CQ ...
- el-cascader中最后一级显示为空在前端处理数据
el-cascader中最后一级显示为空是因为从后端接口获取的数据最后一个children为空 <el-cascader :options="treeDeptData" st ...
- Kafka之配置信息
Kafka之配置信息 一.Broker配置信息 属性 默认值 描述 broker.id 必填参数,broker的唯一标识 log.dirs /tmp/kafka-logs Kafka数据存放的目录 ...
- (数据科学学习手札144)使用管道操作符高效书写Python代码
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,一些比较熟悉pandas的读者 ...
- 在CentOs7虚拟机Linux离线安装mysql5.6(亲测可用)
在该博主博客的的基础上进一步改进:https://blog.csdn.net/zhousq8929/article/details/117223255 文章目录 1.在官网下载mysql-5.6.36 ...
- 齐博x1云市场注意事项
安装云市场应用注意事项 大到频道,小到插件甚至钩子及风格都可以在线安装,在线升级. 但是有一个大家务必注意的地方,就是重装系统后,再安装有可能导致重复收费. 这个问题是可以解决的.当然如果不是重装系统 ...
- 一、SQL介绍
Mysql 简单来说,数据库就是一个存储数据的仓库,它将数据按照特定的规律存储在磁盘上.为了方便用户组织和管理数据,其专门提供了数据库管理系统.通过数据库管理系统,用户可以有效的组织和管理存储在数据库 ...
- vue中动态引入图片为什么要是require, 你不知道的那些事
相信用过vue的小伙伴,肯定被面试官问过这样一个问题:在vue中动态的引入图片为什么要使用require 有些小伙伴,可能会轻蔑一笑:呵,就这,因为动态添加src被当做静态资源处理了,没有进行编译,所 ...
- [苹果APP上架]ios App Store上架详细教程-一条龙顺滑上架-适合小白
如何在 2022 年将您的应用提交到 App Store 您正在启动您的第一个应用程序,或者距离上次已经有一段时间了.作者纸飞机@cheng716051来给你讲讲将应用程序提交到 App Store ...
- iOS开发应用上传AppStore的步骤
原文:http://blog.csdn.net/ayangcool/article/details/46647693 前言:作为一名IOS开发者,把开发出来的App上传到App Store是必须的 ...