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 并发编程相关推荐

  1. C++并发编程之线程异步std::packaged_task知识点总结

    1.std::packaged_task介绍 类模板 std::packaged_task 包装任何可调用 (Callable) 目标(函数. lambda 表达式. bind 表达式或其他函数对象) ...

  2. C++ 并发编程(从C++11到C++17)

    自C++11标准以来,C++语言开始支持多线程模型.借助多线程模型,我们可以开发出更好的并发系统.本文以C++语言为例,讲解如何进行并发编程.并尽可能涉及C++11,C++14以及C++17中的主要内 ...

  3. C++ 并发编程(C++11 到 C++17 )

    为什么要并发编程 大型的软件项目常常包含非常多的任务需要处理.例如:对于大量数据的数据流处理,或者是包含复杂GUI界面的应用程序.如果将所有的任务都以串行的方式执行,则整个系统的效率将会非常低下,应用 ...

  4. C++并发编程(C++11到C++17)转载

    C++并发编程(C++11到C++17)转载 作者:paulquei https://paul.pub/cpp-concurrency/ 为什么要并发编程 大型的软件项目常常包含非常多的任务需要处理. ...

  5. C++并发编程(C++11到C++17)

    为什么要并发编程 大型的软件项目常常包含非常多的任务需要处理.例如:对于大量数据的数据流处理,或者是包含复杂GUI界面的应用程序.如果将所有的任务都以串行的方式执行,则整个系统的效率将会非常低下,应用 ...

  6. C++并发编程线程间共享数据std::future和sd::promise

    线程间共享数据 使用互斥锁实现线程间共享数据 为了避免死锁可以考虑std::lock()或者boost::shared_mutex 要尽量保护更少的数据 同步并发操作 C++标准库提供了一些工具 可以 ...

  7. c+++11并发编程语言,C++11并发编程:多线程std:thread

    原标题:C++11并发编程:多线程std:thread 一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植, ...

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

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

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

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

最新文章

  1. java聚合excel_java操作excel
  2. Android intent 页面跳转
  3. 谷歌控制面板中的NetWrok
  4. python hacklib_【入门】angr:基于python的二进制分析框架
  5. 《掌握需求过程》阅读笔记05
  6. 【浏览器】浏览器下载CSV文件的方法
  7. 星际二 地图制作过程
  8. Javascript第六章基本包装类型第八课
  9. oracle instr函数 判断字段中是否有换行符
  10. node2vec python_论文笔记 | node2vec
  11. JavaScript 设计模式——策略模式
  12. 解决虚拟机中桥接模式下ip地址的设置
  13. 宋宝华: 论一个程序员问问题的自我修养(修订版)
  14. 土法炼钢:怎么实现一个简单的B+Tree In-Disk
  15. 一个困扰程序员很多年的问题:微服务领域SpringCloud这么火,为何还要学习SpringCloud Alibaba?
  16. 大学物理学(第5版)下(第14章)课后习题答案
  17. trans系列是sci几区_如何看SCI期刊属于几区
  18. c#中利用keybd_event函数+自定义软键盘实现中文输入时的问题
  19. 程序员怎么写出亮眼的简历?
  20. 如何安装Python虚拟环境

热门文章

  1. usb serial port 驱动_tty初探 — uart驱动框架分析
  2. js脚本锁计算机软件,JS使用插件cryptojs进行加密解密数据实例
  3. java web junit_如何使用junit测试javaweb工程
  4. rust风化速度_反驳《Golang、Rust的执行速度的对照,让人大吃一惊。》——不会别瞎说...
  5. 微软u盘安装工具_使用微软Winget工具安装软件教程
  6. html 将两个标签绑在一起,基本标签2
  7. Linux 系统应用编程——标准I/O
  8. Error format not a string literal and no format arguments解决方案
  9. ShellExecute 函数的用法和实例
  10. linux kill 关闭进程命令