Taskflow:异步任务(Asynchronous Tasking)
简单使用
tf::Executor 提供了异步执行Task的操作tf::Executor::async,并返回Future,用于保留该函数调用的结果。
#include <taskflow/taskflow.hpp>void print_str(char const* str) {std::cout << str << std::endl;
}int main() {tf::Executor executor;std::future<int> future = executor.async([](){print_str("async task");return 1;});print_str(std::to_string(future.get()).c_str());
}
如果不需要关系返回值,推荐使用Executor::silent_async ,相较于tf::Executor::async,减少了一些状态共享的开销:
#include <taskflow/taskflow.hpp>int main() {tf::Executor executor;executor.silent_async([](){print_str("async task");return 1;});// sleep 1sstd::this_thread::sleep_for(std::chrono::seconds(1));
}
调度器会自动检测异步任务是从外部线程还是工作线程提交的,并使用work stealing来安排其执行:
#include <taskflow/taskflow.hpp>void print_str(char const* str) {std::cout << str << std::endl;
}
int main() {tf::Executor executor;tf::Taskflow taskflow;tf::Task my_task = taskflow.emplace([&executor](){// 从 my_task 内部启动一个异步任务executor.async([&executor](){// 从 一个异步任务内部启动一个异步任务,可能执行在其他worker线程中executor.async([&](){}); });});executor.run(taskflow);executor.wait_for_all(); // 等待所以任务执行结束
}
由分析工具,可以看到,这个嵌套的异步任务分别执行在不同的线程中:
从executor创建的异步任务不属于任何任务流。异步任务的生命周期由创建任务的executor自动管理。
Subflow 创建异步任务
Subflow也可以创建自己的异步任务,Taskflow保证这些异步任务在join前结束:
#include <cassert>
#include <taskflow/taskflow.hpp>void print_str(char const* str) {std::cout << str << std::endl;
}
int main() {tf::Executor executor;tf::Taskflow taskflow;std::atomic<int> counter{0};taskflow.emplace([&executor,&counter](tf::Subflow& sbf){// 创建100个异步任务,注意,这里是Subflow创建的for(int i = 0; i < 100; i++) {sbf.silent_async([&counter](){++counter;});}// 显式调用join,这100个任务将会在这里全部完成sbf.join();assert(counter == 100); // 验证这里的所有异步任务均回收});executor.run(taskflow).wait();taskflow.dump(std::cout);
}
注意: 只可以在一个joinable 的Subflow中创建异步任务,从Detach的Subflow启动异步任务会导致未定义的行为。
为异步任务添加依赖(动态任务图)
动态任务图的优势:
- 探索任务图并行性:在一些应用场景中,任务之间的依赖关系可能不是事先完全确定的,而是会根据程序的运行状态动态变化。通过动态创建任务图,可以根据实时的控制流来调整任务的执行顺序和并行度,从而实现更高效的并行处理。
- 重叠任务图创建时间与任务执行时间:在传统的任务图构建方法中,通常需要先构建完整的任务图,然后再开始执行任务。但在某些情况下,我们可以在创建任务图的同时执行任务,这样可以减少等待任务图完全构建完成的时间,提高整体的执行效率。
创建一个动态任务图
当应用程序中无法构建任务图的运行模型时,可以使用tf::Executor::dependent_async和tf::Executor::silent_dependent_async来动态创建任务图。这种类型的并行性也被称为实时任务图并行性:
#include <taskflow/taskflow.hpp>void print_str(char const* str) {std::cout << str << std::endl;
}
int main() {tf::Executor executor;// silent_dependent_async 返回一个AsyncTask对象,用于构建后续的任务依赖tf::AsyncTask A = executor.silent_dependent_async([&](){print_str("Task A");});// 构建任务依赖,B依赖Atf::AsyncTask B = executor.silent_dependent_async([&](){print_str("Task B");}, A);// 构建任务依赖,C依赖Atf::AsyncTask C = executor.silent_dependent_async([&](){print_str("Task C");}, A);// dependent_async 返回std::pair<tf::AsyncTask, std::future<void>>// D依赖B和C, fuD 来等待任务D执行完成auto [D, fuD] = executor.dependent_async([&](){print_str("Task D");}, B, C);fuD.get(); // 等待D执行完成
}
tf::Executor::dependent_async和tf::Executor::silent_dependent_async都创建一个类型为tf::AsyncTask的任务,以异步运行给定的函数。此外,tf::Executor::dependent_async返回一个std::future,最终保存执行结果。当从两个调用返回时,executor安排worker在满足其依赖项时运行任务。也就是说,任务执行与创建任务图同时进行,而不是如静态图一样,构建完整个图后再执行。
图左展示的是静态图的执行逻辑,先建图,再执行,而图右展示的是动态图的执行逻辑,边建图,边执行。
注意:动态图的构建只允许将当前任务的依赖项与之前创建的任务相关联,因此用户需要保证拓扑顺序的正确性。
另外,如果整个动态图均使用silent_dependent_async构建,也可以使用executor.wait_for_all()来保证所有异步任务均会完成。
int main() {tf::Executor executor;tf::AsyncTask A = executor.silent_dependent_async([](){ printf("A\n"); });tf::AsyncTask B = executor.silent_dependent_async([](){ printf("B\n"); }, A);tf::AsyncTask C = executor.silent_dependent_async([](){ printf("C\n"); }, A);tf::AsyncTask D = executor.silent_dependent_async([](){ printf("D\n"); }, B, C);executor.wait_for_all();
}
如果一个任务的依赖是依靠运行时的结果得出的,而不能显式指定,可以使用dependent_async和silent_dependent_async的重载,来动态确定:
#include <cstdio>
#include <taskflow/taskflow.hpp>void print_str(char const* str) {std::cout << str << std::endl;
}
int main() {tf::Executor executor;std::vector<tf::AsyncTask> dependents; // 存放动态依赖int n = 0;printf("input n:");scanf("%d",&n); // 动态输入依赖数for(int i = 0; i < n; i++) {dependents.push_back(executor.silent_dependent_async([i](){char name[16];std::sprintf(name,"Task:%c\n", 'A'+i);print_str(name); })); // 创建异步任务,并添加到dependents中}executor.silent_dependent_async([](){print_str("All dependents completed\n");}, dependents.begin(), dependents.end()); // 创建异步任务,等待所有依赖完成executor.wait_for_all();
}
因为printf不是线程安全的,所以打印可能并不符合预期,但是执行顺序是正确的。
下图是n=8时的性能分析图:
可以看到,虽然依赖是运行时才确定的,但是任务的调度仍然保持着较高的并行度。
tf::AsyncTask是一个轻量级句柄,保留由executor创建的依赖异步任务的共享所有权。这种共享所有权确保异步任务在将其添加到另一个异步任务的依赖项列表中时保持活动状态,从而避免了经典的ABA问题。
// main thread retains shared ownership of async task A
tf::AsyncTask A = executor.silent_dependent_async([](){});// task A remains alive (i.e., at least one ref count by the main thread)
// when being added to the dependency list of async task B
tf::AsyncTask B = executor.silent_dependent_async([](){}, A);
注意: executor.silent_dependent_async和executor.dependent_async是线程安全的,即可以在多线程环境下安全地添加任务,只要拓扑上的任务在逻辑上是正确的。
同时,tf::AsyncTask也包含类似std::future一样的is_done方法,可重入future。
// create a dependent async task that returns 100
auto [task, fu] = executor.dependent_async([](){ return 100; });// loops until the dependent async task completes
while(!task.is_done());
assert(fu.get() == 100);
动态任务图计算斐波那契数列
有了动态任务图,就可以很方便地将一些递归任务拆解成并行任务:
#include <cstdio>
#include <taskflow/taskflow.hpp>
#include <tuple>void print_str(char const* str) {std::cout << str << std::endl;
}
int main() {tf::Executor executor;int n = 0;printf("input n:");scanf("%d",&n); // 动态输入依赖数std::function<int(int)> fibonacci; fibonacci = [&](int N){if(N < 2) {return N;}auto [t1, fu1] = executor.dependent_async(std::bind(fibonacci, N-1));auto [t2, fu2] = executor.dependent_async(std::bind(fibonacci, N-2));// worker内等待必须是corun_until,否则会死锁executor.corun_until([&](){ return t1.is_done() && t2.is_done(); });return fu1.get() + fu2.get();};auto [task, fib11] = executor.dependent_async(std::bind(fibonacci, n));std::cout<<"fibonacci("<<n<<") = "<<fib11.get()<<std::endl;
}
当n=5时,性能分析图如下:
相关文章:

Taskflow:异步任务(Asynchronous Tasking)
简单使用 tf::Executor 提供了异步执行Task的操作tf::Executor::async,并返回Future,用于保留该函数调用的结果。 #include <taskflow/taskflow.hpp>void print_str(char const* str) {std::cout << str << std::endl; }int main() …...

学习鸿蒙基础(9)
目录 一、鸿蒙国际化配置 二、鸿蒙常用组件介绍 三、鸿蒙像素单位介绍 四、鸿蒙布局介绍 1、Row与Column线性布局 2、层叠布局-Stack 3、弹性布局 4、栅格布局 5、网格布局 一、鸿蒙国际化配置 base目录下为默认的string。en_US对应美国的。zh_CN对应中国的。新增一个s…...
spring boot的小数位丢失.00 或者.0
1、背景 在使用spring boot时,前端的界面展示的数据是2 ,在数据库中存储的是小数。但是导出Excel的时候数据是 2.00 。奇了怪了为啥会不一样,数据都是一样的没有做过处理。 2、排查问题 经过层层的debug 发现数据库返回的数据是2.00&#x…...
nginx如何清理页面缓存
在 Nginx 中,清理页面缓存通常涉及配置缓存头以控制缓存行为,或者使用外部工具或机制来清除缓存。以下是一些建议来管理和清理 Nginx 的页面缓存: 配置缓存头: Nginx 本身不直接提供缓存机制,但可以通过配置 proxy_cac…...

深度学习pytorch——经典卷积网络之ResNet(持续更新)
错误率前五的神经网络(图-1): 图-1 可以很直观的看到,随着层数的增加Error也在逐渐降低,因此深度是非常重要的,但是学习更好的网络模型和堆叠层数一样简单吗?通过实现表明(图-2&…...

react 面试题(2024 最新版)
1. 对 React 的理解、特性 React 是靠数据驱动视图改变的一种框架,它的核心驱动方法就是用其提供的 setState 方法设置 state 中的数据从而驱动存放在内存中的虚拟 DOM 树的更新 更新方法就是通过 React 的 Diff 算法比较旧虚拟 DOM 树和新虚拟 DOM 树之间的 Chan…...

JVM(三)——字节码技术
三、字节码技术 1、类文件结构 一个简单的 HelloWorld.java package com.mysite.jvm.t5; // HelloWorld 示例 public class HelloWorld {public static void main(String[] args) {System.out.println("hello world");} }执行 javac -parameters -d . HellowWorld.…...

HarmonyOS 应用开发之Stage模型绑定FA模型ServiceAbility
本小节介绍Stage模型的两种应用组件如何绑定FA模型ServiceAbility组件。 UIAbility关联访问ServiceAbility UIAbility关联访问ServiceAbility和UIAbility关联访问ServiceExtensionAbility的方式完全相同。 import common from ohos.app.ability.common; import hilog from o…...

高效解决Visual Studio无法识别到自定义头文件
文章目录 问题解决方案 问题 说明你没有好好配置项目属性 解决方案 把头文件都集中存放到一个文件夹里 之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路…...
[数据集][目标检测]道路行人车辆坑洞锥形桶检测数据集VOC+YOLO格式6275张4类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):6275 标注数量(xml文件个数):6275 标注数量(txt文件个数):6275 标注…...

风险与收益
风险与收益 影响资产需求的主要因素财富总量预期收益率资产的流动性影响流动性的主要因素 风险 如何降低风险系统风险和非系统风险机会集合与有效集合资产组合理论 影响资产需求的主要因素 影响资产需求的主要因素包括:财富总量、预期收益率、资产的流动性和风险。…...

linux服务器安装mysql8
1.下载MYSQL 近几天在linux服务器已安装过2次mysql8,亲测有效,没有遇到任何问题,文档已写的很清楚,按步骤来即可。如果按文档有遇到要使用yum命令的话,需要服务器开通外网。 1.1官网下载 进入官网下拉到最后&#x…...

亚信安全荣获2023年度5G创新应用评优活动两项大奖
近日,“关于2023 年度5G 创新应用评优活动评选结果”正式公布,亚信安全凭借在5G安全领域的深厚积累和创新实践,成功荣获“5G技术创新的优秀代表”和“5G应用创新的杰出实践”两项大奖。 面向异构安全能力的5G安全自动化响应系统 作为5G技术创…...

linux之忘记root密码
一,开机到如下地方按下e进入紧急模式 然后再如下位置输入init/bin/bash 然后Ctrlx 二, 修改密码 以上操作分别为 1),重新挂载根目录 mount -o remount,rw / 2),修改密码 passwd root 3)&a…...

jspm智能仓储系统
开发语言:Java 框架:ssm 技术:JSP JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7(一定要5.7版本) 数据库工具:Navicat11 开发软件:eclipse/myeclip…...

深入理解数据结构(3):栈和队列详解
文章主题:顺序表和链表详解🌱所属专栏:深入理解数据结构📘作者简介:更新有关深入理解数据结构知识的博主一枚,记录分享自己对数据结构的深入解读。😄个人主页:[₽]的个人主页&#x…...

单例设计模式(3)
单例模式(3) 实现集群环境下的分布式单例类 如何理解单例模式中的唯一性? 单例模式创建的对象是进程唯一的。以springboot应用程序为例,他是一个进程,可能包含多个线程,单例代表在这个进程的某个类是唯一…...

将jupyter notebook文件导出为pdf(简单有效)
1.打开jupyter notebook笔记: 2.点击file->print Preview 3.在新打开的页面右键打印 4.另存为PDF 5.保存即可 6.pdf效果 (可能有少部分图片显示不了) 网上也有其他方法,比如将其转换为.tex再转为PDF等,但个人觉…...
使用INSERT INTO ... ON DUPLICATE KEY UPDATE批量插入更新导入excel数据的实践场景应用
INSERT INTO ... ON DUPLICATE KEY UPDATE 是 MySQL 中的一个非常有用的语法,它允许你在插入新记录时,如果记录的唯一键(如主键或唯一索引)已存在,则执行更新操作而不是插入。这可以帮助你避免在插入数据时产生的重复键…...

AJAX-项目优化(目录、基地址、token、请求拦截器)
目录管理 基地址存储 在utils/request.js配置axios请求基地址 作用:提取公共前缀地址,配置后axios请求时都会baseURLurl 填写API的公共前缀后,将js文件导入到html文件中 <script src"../../utils/request.js"></script&…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...