Taskflow:子流任务(Subflow Tasking)

news2024/11/28 20:57:25

创建Subflow

DAG任务中,有一种常见的场景,一个任务可能在执行期间产生新的任务,然后紧接着执行新任务。 之前提到的静态图就没有办法实现这样一个功能了,所以Taskflow提供了另一种流的节点:Subflow,Subflow的API与Taskflow无异,但又可以作为Taskflow的一个节点。

比如描述如下依赖图:
在这里插入图片描述

#include <memory>
#include <taskflow/taskflow.hpp>
int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;

    tf::Task A = taskflow.emplace([] () {}).name("A");  // static task A
    tf::Task C = taskflow.emplace([] () {}).name("C");  // static task C
    tf::Task D = taskflow.emplace([] () {}).name("D");  // static task D

    // 通过lambda创建subflow
    // 开始执行的时候,会创建一个subflow,然后通过引用传给lambda
    // 只有当本subflow执行完成之后,才会执行taskflow
    tf::Task B = taskflow.emplace([] (tf::Subflow& subflow) { 
        tf::Task B1 = subflow.emplace([] () {}).name("B1");  // subflow task B1
        tf::Task B2 = subflow.emplace([] () {}).name("B2");  // subflow task B2
        tf::Task B3 = subflow.emplace([] () {}).name("B3");  // subflow task B3
        B1.precede(B3);  // B1 runs bofore B3
        B2.precede(B3);  // B2 runs before B3
    }).name("B");

    A.precede(B);  // B runs after A
    A.precede(C);  // C runs after A
    B.precede(D);  // D runs after B
    C.precede(D);  // D runs after C

    taskflow.dump(std::cout);      // 在执行前,subflow无法展开,subflow只会显示节点B
    executor.run(taskflow).get();  // execute the graph to spawn the subflow

    taskflow.dump(std::cout);      // 执行完毕后,才可以完全展开
    return 0;
}

在run之前dump,subflow只会被当作普通节点:

在这里插入图片描述

在run之后调用,subflow被展开,得到真正的依赖图:
在这里插入图片描述

Join a Subflow

Subflow 在离开其上下文时默认调用join,表示需要把subflow中的task执行完,才完成subflow的执行。同时,还可以在上下文中显式调用join,来完成递归模式:

#include <memory>
#include <taskflow/taskflow.hpp>


// 递归计算斐波那契数列
int spswm(int n, tf::Subflow& sbf) {
    if(n < 2) return n;
    int res1 = 0, res2 = 0;

    // 生成两个递归子任务.
    sbf.emplace([&res1, n](tf::Subflow& sbf_inner){
        res1 = spswm(n-1, sbf_inner);
    }).name("sub Task:_"+std::to_string(n-1));
    sbf.emplace([&res2, n](tf::Subflow& sbf_inner){
        res2 = spswm(n-2, sbf_inner);
    }).name("sub Task:_"+std::to_string(n-2));

    // 显式调用join,得到两个子任务的返回值
    sbf.join();
    return res1 + res2; 
}
int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;

    int res = 0; // 用于存放最后的结果
    taskflow.emplace([&res](tf::Subflow& sbf){
        res = spswm(5, sbf); // 计算5的斐波那契数
    }).name("main Task");

    executor.run(taskflow).wait();
    std::cout << "5的斐波那契数:" << res << std::endl;

    taskflow.dump(std::cout);    
    return 0;
}

调用图如下:

在这里插入图片描述

Detach a Subflow

和线程一样,Subflow 可以Detach出去,单独执行(并最后被主Taskflow Join)

#include <taskflow/taskflow.hpp>

int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;
    tf::Task A = taskflow.emplace([] () {}).name("A");  // static task A
    tf::Task C = taskflow.emplace([] () {}).name("C");  // static task C
    tf::Task D = taskflow.emplace([] () {}).name("D");  // static task D

    tf::Task B = taskflow.emplace([] (tf::Subflow& subflow) { 
    tf::Task B1 = subflow.emplace([] () {}).name("B1");  // static task B1
    tf::Task B2 = subflow.emplace([] () {}).name("B2");  // static task B2
    tf::Task B3 = subflow.emplace([] () {}).name("B3");  // static task B3
        B1.precede(B3);    // B1 runs bofore B3
        B2.precede(B3);    // B2 runs before B3
        subflow.detach();  // 分离出Taskflow,单独执行
    }).name("B");

    A.precede(B);  // B runs after A
    A.precede(C);  // C runs after A
    B.precede(D);  // D runs after B
    C.precede(D);  // D runs after C

    executor.run(taskflow).wait();
    taskflow.dump(std::cout);    
    return 0;
}

最终结构如下:
在这里插入图片描述

detach出去的Subflow是临时的,所以,如果执行的是run_n, ABCD四个节点只会构造一次,但是subflow会被构造多次:

#include <taskflow/taskflow.hpp>

int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;
    tf::Task A = taskflow.emplace([] () {}).name("A");  // static task A
    tf::Task C = taskflow.emplace([] () {}).name("C");  // static task C
    tf::Task D = taskflow.emplace([] () {}).name("D");  // static task D

    tf::Task B = taskflow.emplace([] (tf::Subflow& subflow) { 
    tf::Task B1 = subflow.emplace([] () {}).name("B1");  // static task B1
    tf::Task B2 = subflow.emplace([] () {}).name("B2");  // static task B2
    tf::Task B3 = subflow.emplace([] () {}).name("B3");  // static task B3
        B1.precede(B3);    // B1 runs bofore B3
        B2.precede(B3);    // B2 runs before B3
        subflow.detach();  // 分离出Taskflow,单独执行
    }).name("B");

    A.precede(B);  // B runs after A
    A.precede(C);  // C runs after A
    B.precede(D);  // D runs after B
    C.precede(D);  // D runs after C

    executor.run_n(taskflow, 5).wait();
    assert(taskflow.num_tasks() == 19);
    taskflow.dump(std::cout);
    return 0;
}

在这里插入图片描述

嵌套子图

Subflow 支持递归,也支持嵌套:

#include <taskflow/taskflow.hpp>

int main() {
    tf::Taskflow taskflow;

    tf::Task A = taskflow.emplace([] (tf::Subflow& sbf){
        std::cout << "A spawns A1 & subflow A2\n";
        tf::Task A1 = sbf.emplace([] () {
            std::cout << "subtask A1\n";
        }).name("A1");

        tf::Task A2 = sbf.emplace([] (tf::Subflow& sbf2){
            std::cout << "A2 spawns A2_1 & A2_2\n";
            tf::Task A2_1 = sbf2.emplace([] () {
                std::cout << "subtask A2_1\n";
            }).name("A2_1");
            tf::Task A2_2 = sbf2.emplace([] () {
                std::cout << "subtask A2_2\n";
            }).name("A2_2");
            A2_1.precede(A2_2);
        }).name("A2");
        A1.precede(A2);
    }).name("A");

    // execute the graph to spawn the subflow
    tf::Executor().run(taskflow).get();
    taskflow.dump(std::cout);
}

在这里插入图片描述

同样,也可以detach 子图的子图,独立执行,最终都会被master Taskflow 统一Join(类似进程与子进程的关系)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1556412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多张图片怎么合成一张gif?快来试试这个方法

将多张图片合成一张gif动图是现在常见的图像处理的方式&#xff0c;适合制作一些简单的动态图片。通过使用在线图片合成网站制作的gif动图不仅体积小画面丰富&#xff0c;画质还很清晰。不需要下载任何软件小白也能轻松上手&#xff0c;支持上传jpg、png以及gif格式图片&#x…

在同一个网站上自动下载多个子页面内容

一、问题现象 第一次遇到这样的问题&#xff0c;如下图&#xff1a; 即在同一个网站上下载多个内容时&#xff0c;第一个内容明明已经正常get到了&#xff0c;但开始第二个页面的查询 以后&#xff0c;原来已经查出的内容就找不到了。 二、解决办法 我不知道大家是不是遇到…

meanshift论文学习

1. abstract 2. 理论解读 目标函数 然后对(11)求导&#xff0c;求解x&#xff0c;x实际就是求解当图像位置的值&#xff0c;求导之后表示为&#xff1a; 进一步整理得&#xff1a; 上式第二项即为meanshift 进一步整理为 上式表明了均值漂移与核函数之间的关系。 3. 缺点…

多语言多货币多入口FecMall跨境电商B2C商城系统源码

FecMall是一套多语言多货币多入口的开源电商 B2C 商城&#xff0c;支持移动端vue, app, html5&#xff0c;微信小程序微店&#xff0c;微信小程序商城等。很适合用来做跨境电商外贸独立站。 Fecmall 全称为Fancy ECommerce Mall&#xff0c;是基于php Yii2框架之上开发的一款优…

基于ssm中国咖啡文化宣传网站的设计与实现论文

摘 要 本课题是根据咖啡文化宣传需要以及网络的优势建立的一个中国咖啡文化宣传网站&#xff0c;来实现中国咖啡文化宣传以及咖啡商品售卖的功能。 本中国咖啡文化宣传网站应用Java技术&#xff0c;MYSQL数据库存储数据&#xff0c;基于SSMVue框架开发。在网站的整个开发过程中…

Filter、Listener、AJAX、Vue、Element

Filter 概念&#xff1a;Filter 表示过滤器&#xff0c;是JavaWeb三大组件(Servlet、Filter、 Listener)之一。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;权限控制、统一编码处理、敏感…

Gitea 的详细介绍

什么是 Gitea&#xff1f; Gitea 是一个开源、轻量级的自托管 Git 服务&#xff0c;它允许用户搭建类似于 GitHub 或 GitLab 的代码托管平台。由于采用 Go 语言开发&#xff0c;Gitea 具有高效的性能和跨平台特性&#xff0c;适合个人开发者或小团队使用。 Gitea 的特点 轻量…

pycharm修改主题颜色和注释颜色

目录 一、修改主题颜色 二、修改注释颜色 一、修改主题颜色 总结的来说就是&#xff1a;File-Settings-Appearance-Theme。 有三种主题&#xff1a; Darcula&#xff1a;默认主题&#xff0c;可以看作是黑的&#xff1a; IntelliJ Light:可以看作是白的&#xff1a; High con…

在新能源充电桩、智能充电枪、储能等产品领域得到广泛应用的两款微功耗轨至轨运算放大器芯片——D8541和D8542

D8541和D8542是我们推荐的两款微功耗轨至轨运算放大器芯片&#xff0c;其中D8541为单运放&#xff0c; D8542为双运放&#xff0c;它特别适用于NTC温度采集电路、ADC基准电压电路、有源滤波器、电压跟随器、信号放大器等电路应用&#xff0c;在新能源充电桩、智能充电枪、…

JavaScript练手小技巧:仿米哈游官网人物跟随鼠标位移效果

最近&#xff0c;有同学找到我&#xff0c;说&#xff1a;老师&#xff0c;我想模仿米哈游官网。 我说&#xff1a;可以&#xff0c;很不错的。 她说&#xff1a;有些效果有点难&#xff0c;能不能帮我看下。 于是&#xff0c;我就简单大概粗糙的讲解了下大致的原理&#xf…

unity学习(78)--unity调试--长痛不如短痛

1.在vs2022中&#xff0c;工具--获取工具与功能。 2. 安装图中工具&#xff0c;原来我早就安装了。 3 f9下断 同时点击图中按钮 vs此时变为如下状态 unity中出现如下提示&#xff1a; 4 在unity中运行游戏&#xff0c;vs这边确实成功断住了&#xff01;

柔数组的介绍

柔数组简单介绍 这个词你可能没有听过但是他的确是存在的。 1.在c99中结构中的最后⼀个元素允许是未知⼤⼩的数组&#xff0c;这就叫做『柔性数组』成员 2这就代表了它存在与结构体中&#xff0c;很重要的一点是&#xff0c;他只能是结构体的最后的一个成员&#xff0c;这是…

面向对象特征二:继承

继承的概述 生活中的继承 财产继承&#xff1a; 绿化&#xff1a;前人栽树&#xff0c;后人乘凉 “绿水青山&#xff0c;就是金山银山” 样貌&#xff1a; 继承之外&#xff0c;是不是还可以"进化"&#xff1a; 继承有延续&#xff08;下一代延续上一代的基因、财…

动态内存管理【malloc,calloc,realloc和free的理解】【柔性数组的概念】

一.为什么要有动态内存分配 我们知道&#xff0c;当我们创建变量的时候&#xff0c;我们会向系统申请一定大小的空间内存。比如int a10或者int arr[10]&#xff1b;我就向内存申请了4或者40个字节的大小来存放数据。但是当我们一旦申请好这个空间&#xff0c;大小就无法调整了…

Linux部署Kafka2.8.1

安装Jdk 首先确保你的机器上安装了Jdk&#xff0c;Kafka需要Java运行环境&#xff0c;低版本的Kafka还需要Zookeeper&#xff0c;我此次要安装的Kafka版本为2.8.1&#xff0c;已经内置了一个Zookeeper环境&#xff0c;所以我们可以不部署Zookeeper直接使用。 1、解压Jdk包 t…

缓存和缓存的常用使用场景

想象一下,一家公司在芬兰 Google Cloud 数据中心的服务器上托管一个网站。对于欧洲用户来说,加载可能需要大约 100 毫秒,但对于墨西哥用户来说,加载需要 3-5 秒。幸运的是,有一些策略可以最大限度地减少远程用户的请求延迟。 这些策略称为缓存和内容交付网络 (CDN),它们是…

HarmonyOS实战开发-使用List组件实现导航与内容联动的效果。

1 卡片介绍 使用ArkTS语言&#xff0c;实现一个导航与内容二级联动的效果。 2 标题 二级联动&#xff08;ArkTS&#xff09; 3 介绍 本篇Codelab是主要介绍了如何基于List组件实现一个导航和内容的二级联动效果。样例主要包含以下功能&#xff1a; 切换左侧导航&#xff…

OSCP靶场--Sorcerer

OSCP靶场–Sorcerer 考点(feroxbuster目录扫描zip包隐藏文件发现公钥私钥公钥覆盖私钥登陆suid start-stop-daemon提权) 1.nmap扫描 ## ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.216.100 -sV -sC -Pn --min-rate 2500 -p- Starting Nmap 7.92 ( https://nmap.or…

nodejs下载安装以及npm、yarn安装及配置教程

1、nodejs下载安装 ​ 1.1、使用nodejs版本管理工具下载安装&#xff0c;可一键安装、切换不同nodejs版本&#xff0c; nvm-setup.zip&#xff1a;安装版&#xff0c;推荐使用 本次演示的是安装版。 1、双击安装文件 nvm-setup.exe 选择nvm安装路径 例如&#xff1a;E:\Soft…

STL中容器、算法、迭代器

STL标准模板库封装了常用的数据结构和算法&#xff0c;让程序员无需太关心真实的数据结构实现。 容器 容器&#xff1a;用来存放数据的。 STL容器就是将运用最广泛的的一些数据结构实现出来。 常用的数据结构有&#xff1a;数组、链表、树、栈、队列、集合、映射表。 这些…