std::packaged_task() ---C++17 并发编程
std::packaged_task() —C++17 并发编程
std::packaged_task<>
连结了future
对象与函数(或可调用对象)。
std::packaged_task<>
对象在执行任务时,会调用关联的函数(或可调用对象),把返回值保存为future
的内部数据,并令future
准备就绪。它可作为线程池的构件单元,亦可用于其他任务管理方案。
例如,为各个任务分别创建专属的独立运行的线程,或者在某个特定的后台线程上依次执行全部任务。若一项庞杂的操作能分解为多个子任务,则可把它们分别包装到多个std::packaged_task<>
实例之中,再传递给任务调度器或线程池。这就隐藏了细节,使任务抽象化,让调度器得以专注处理std::packaged_task<>
实例,无须纠缠于形形色色的任务函数。
std::packaged_task<>
是类模板,其模板参数是函数签名(function signature)
:譬如,void()
表示一个函数,不接收参数,也没有返回值;又如,int(std::string&,double*
)代表某函数,它接收两个参数并返回int
值,其中,第一个参数是非const
引用,指向std::string
对象,第二个参数是double
类型的指针。
假设,我们要构建std::packaged_task<>
实例,那么,由于模板参数先行指定了函数签名,因此传入的函数(或可调用对象)必须与之相符,即它应接收指定类型的参数,返回值也必须可以转换为指定类型。
这些类型不必严格匹配,若某函数接收int
类型参数并返回float
值,我们则可以为其构建std::packaged_task<double(double)>
的实例,因为对应的类型可进行隐式转换。
类模板std::packaged_task<>
具有成员函数get_future()
,它返回std::future<>
实例,该future
的特化类型取决于函数签名所指定的返回值。
std::packaged_task<>
还具备函数调用操作符,它的参数取决于函数签名的参数列表。
std::packaged_task
对象是可调用对象,我们可以直接调用,还可以将其包装在std::function
对象内,当作线程函数传递给std::thread
对象,也可以传递给需要可调用对象的函数。
若std::packaged_task
作为函数对象而被调用,它就会通过函数调用操作符接收参数,并将其进一步传递给包装在内的任务函数,由其异步运行得出结果,并将结果保存到std::future
对象内部,再通过get_future()
获取此对象。因此,为了在未来的适当时刻执行某项任务,我们可以将其包装在std::packaged_task
对象内,取得对应的future
之后,才把该对象传递给其他线程,由它触发任务执行。
等到需要使用结果时,我们静候future
准备就绪即可。
举一个不是很恰当的例子:
#pragma once
#include <deque>
#include <future>
#include <mutex>
#include <thread>
#include <utility>
#include <iostream>using namespace std;/** 模拟多线程加载 GUI 界面*/
std::mutex mt;
std::deque<std::packaged_task<std::string()>> tasks;
std::vector<future<std::string>> futures;void gui_thread()
{for (int i = 0; i < 3; ++i){std::packaged_task<std::string()> task;{std::unique_lock<std::mutex> lk(mt);if (tasks.empty())continue;task = std::move(tasks.front());tasks.pop_front();}task();}}template <typename Func>
std::future<std::string> post_task_for_gui(Func f)
{std::packaged_task<std::string()> task(f);std::future<std::string> res = task.get_future();std::lock_guard<std::mutex> lk(mt);tasks.push_back(std::move(task));return res;
}void start_422()
{for (int i = 0; i < 3; ++i){futures.push_back(post_task_for_gui([=]()-> std::string{std::this_thread::sleep_for(3s);return std::format("第 {} 任务执行完毕", i);}));}cout << "任务提交完成" << endl;std::thread gui_background_thread(gui_thread);if (gui_background_thread.joinable())gui_background_thread.detach(); //后台执行for (auto& value : futures){cout << value.get() << endl;}
}
本例采用std::packaged_task<void()>
表示任务,包装某个函数(或可调用对象),它不接收参数,返回void
(倘若真正的任务函数返回任何其他类型的值,则会被丢弃)。
这里,我们采用最简单的任务举例,但前文已提过,std::packaged_task
也能用于更复杂的情况。针对不同的任务函数,std::packaged_task
的函数调用操作符须就此修改参数,保存于相关的future
实例内的返回值类型也须变动,而我们只要通过模板参数,指定对应任务的函数签名即可。
我们可轻松扩展上例,改动那些只准许在GUI线程上运行的任务,令其接收参数,并凭借std::future
返回结果,std::future
不再局限于充当指标,示意任务是否完成。
std::packaged_task() ---C++17 并发编程相关推荐
- C++并发编程之线程异步std::packaged_task知识点总结
1.std::packaged_task介绍 类模板 std::packaged_task 包装任何可调用 (Callable) 目标(函数. lambda 表达式. bind 表达式或其他函数对象) ...
- C++ 并发编程(从C++11到C++17)
自C++11标准以来,C++语言开始支持多线程模型.借助多线程模型,我们可以开发出更好的并发系统.本文以C++语言为例,讲解如何进行并发编程.并尽可能涉及C++11,C++14以及C++17中的主要内 ...
- C++ 并发编程(C++11 到 C++17 )
为什么要并发编程 大型的软件项目常常包含非常多的任务需要处理.例如:对于大量数据的数据流处理,或者是包含复杂GUI界面的应用程序.如果将所有的任务都以串行的方式执行,则整个系统的效率将会非常低下,应用 ...
- C++并发编程(C++11到C++17)转载
C++并发编程(C++11到C++17)转载 作者:paulquei https://paul.pub/cpp-concurrency/ 为什么要并发编程 大型的软件项目常常包含非常多的任务需要处理. ...
- C++并发编程(C++11到C++17)
为什么要并发编程 大型的软件项目常常包含非常多的任务需要处理.例如:对于大量数据的数据流处理,或者是包含复杂GUI界面的应用程序.如果将所有的任务都以串行的方式执行,则整个系统的效率将会非常低下,应用 ...
- C++并发编程线程间共享数据std::future和sd::promise
线程间共享数据 使用互斥锁实现线程间共享数据 为了避免死锁可以考虑std::lock()或者boost::shared_mutex 要尽量保护更少的数据 同步并发操作 C++标准库提供了一些工具 可以 ...
- c+++11并发编程语言,C++11并发编程:多线程std:thread
原标题:C++11并发编程:多线程std:thread 一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植, ...
- C++11 并发指南四(future 详解二 std::packaged_task 介绍)
上一讲<C++11 并发指南四(<future> 详解一 std::promise 介绍)>主要介绍了 <future> 头文件中的 std::promise 类, ...
- C++并发编程之std::async(), std::future, std::promise, std::packaged_task
c++11中增加了线程,使得我们可以非常方便的创建线程,它的基本用法是这样的: void f(int n); std::thread t(f, n + 1); t.join(); 但是线程毕竟是属于比 ...
最新文章
- java聚合excel_java操作excel
- Android intent 页面跳转
- 谷歌控制面板中的NetWrok
- python hacklib_【入门】angr:基于python的二进制分析框架
- 《掌握需求过程》阅读笔记05
- 【浏览器】浏览器下载CSV文件的方法
- 星际二 地图制作过程
- Javascript第六章基本包装类型第八课
- oracle instr函数 判断字段中是否有换行符
- node2vec python_论文笔记 | node2vec
- JavaScript 设计模式——策略模式
- 解决虚拟机中桥接模式下ip地址的设置
- 宋宝华: 论一个程序员问问题的自我修养(修订版)
- 土法炼钢:怎么实现一个简单的B+Tree In-Disk
- 一个困扰程序员很多年的问题:微服务领域SpringCloud这么火,为何还要学习SpringCloud Alibaba?
- 大学物理学(第5版)下(第14章)课后习题答案
- trans系列是sci几区_如何看SCI期刊属于几区
- c#中利用keybd_event函数+自定义软键盘实现中文输入时的问题
- 程序员怎么写出亮眼的简历?
- 如何安装Python虚拟环境
热门文章
- usb serial port 驱动_tty初探 — uart驱动框架分析
- js脚本锁计算机软件,JS使用插件cryptojs进行加密解密数据实例
- java web junit_如何使用junit测试javaweb工程
- rust风化速度_反驳《Golang、Rust的执行速度的对照,让人大吃一惊。》——不会别瞎说...
- 微软u盘安装工具_使用微软Winget工具安装软件教程
- html 将两个标签绑在一起,基本标签2
- Linux 系统应用编程——标准I/O
- Error format not a string literal and no format arguments解决方案
- ShellExecute 函数的用法和实例
- linux kill 关闭进程命令