首页 > 其他分享 >Taskflow 简单使用

Taskflow 简单使用

时间:2024-03-29 21:31:18浏览次数:22  
标签:executor emplace 使用 taskflow put Task 简单 tf Taskflow

Hello World

#include <taskflow/taskflow.hpp>

int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;
    // 返回一个std::tuple<tf::Task, tf::Task, tf::Task, tf::Task> 
    auto [A, B, C, D] = taskflow.emplace(
        [](){std::cout<<"A"<<std::endl;},
        [](){std::cout<<"B"<<std::endl;},
        [](){std::cout<<"C"<<std::endl;},
        [](){std::cout<<"D"<<std::endl;}
    );

    A.precede(B, C);
    D.succeed(B, C);
    executor.run(taskflow).wait();
    

    return 0;
}

内置一个性能profiler,使用方式:

TF_ENABLE_PROFILER=simple.json ./simple # 可执行文件
cat simple.json 

然后把内容复制到TFProf

创建一个Subflow Graph

流程如下:
在这里插入图片描述

#include <taskflow/taskflow.hpp>

void put(const std::string& str) {
    std::cout<<str<<std::endl;
}

int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;
    // 构建三个空任务,并命名
    tf::Task A = taskflow.emplace([](){put("A");}).name("A");
    tf::Task C = taskflow.emplace([](){put("C");}).name("C");
    tf::Task D = taskflow.emplace([](){put("D");}).name("D");

    // 构建一个子流,并命名
    tf::Task B = taskflow.emplace([](tf::Subflow& subflow){
        auto [B1, B2, B3] = subflow.emplace(
            [](){put("B1");},
            [](){put("B2");},
            [](){put("B3");} 
        );
        B3.succeed(B1, B2);
    }).name("B");

    A.precede(B, C);
    D.succeed(B, C);
    executor.run(taskflow).wait();
    

    return 0;
}

控制流

循环执行条件,直到返回true,才执行下一步:
在这里插入图片描述

#include <taskflow/taskflow.hpp>

void put(const std::string& str) {
    std::cout<<str<<std::endl;
}

int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;
    // 构建三个空任务,并命名
    tf::Task init = taskflow.emplace([](){put("init");}).name("init");
    tf::Task stop = taskflow.emplace([](){put("stop");}).name("stop");
    tf::Task cond = taskflow.emplace(
        [](){
            int p = std::rand() % 2;
            put(std::to_string(p).c_str());
            return p;
        }
    ).name("cond");

    init.precede(cond);
    cond.precede(cond,stop); // cond 需要返回True后才解除对cond的依赖
    executor.run(taskflow).wait();
    return 0;
}

任务组

一个taskflow的流程中,还可以嵌入另一个taskflow。
在这里插入图片描述

#include "taskflow/core/taskflow.hpp"
#include <taskflow/taskflow.hpp>

void put(const std::string& str) {
    std::cout<<str<<std::endl;
}


int main() {
    tf::Executor executor; 
    tf::Taskflow f1, f2;
    
    tf::Task f1a = f1.emplace([](){put("f1a");}).name("f1a");
    tf::Task f1b = f1.emplace([](){put("f1b");}).name("f1b");
    tf::Task f1_task_module = f2.composed_of(f1).name("f1_task_module"); // 表示f1是f2的一个task

    tf::Task f2a = f2.emplace([](){put("f2a");}).name("f2a");
    tf::Task f2b = f2.emplace([](){put("f2b");}).name("f2b");
    tf::Task f2c = f2.emplace([](){put("f2c");}).name("f2c");

    f1_task_module.succeed(f2a, f2b).precede(f2c); // 在f2c之前,在f2a, f2b之后
    
    executor.run(f2).wait();
  
    return 0;
}

异步任务

taskflow 支持开启异步任务,动态探索并行度:

#include "taskflow/core/async_task.hpp"
#include "taskflow/core/task.hpp"
#include "taskflow/core/taskflow.hpp"
#include <future>
#include <taskflow/taskflow.hpp>

// 线程不安全,可能会出现打印异常
void put(const std::string& str) {
    std::cout<<str<<std::endl;
}


int main() {
    tf::Executor executor; 
    tf::Taskflow taskflow;
    
    // 第一种方式,通过async的方式注册,并将解决使用future传递
    std::future<int> future = executor.async([](){
        put("async task returns 1");
        return 1;
    }); 

    // 第二种方式,丢弃返回值的异步
    executor.silent_async([](){
        put("async task does not return");
    });

    // 第三种方式,创建异步,并进行动态依赖
    tf::AsyncTask A = executor.silent_dependent_async([](){put("A");});
    tf::AsyncTask B = executor.silent_dependent_async([](){put("B");}, A); // B依赖A
    tf::AsyncTask C = executor.silent_dependent_async([](){put("C");}, A); // C依赖A
    tf::AsyncTask D = executor.silent_dependent_async([](){put("D");}, B, C); // D依赖B和C

    executor.wait_for_all(); // 等待所有异步任务结束
    return 0;
}

执行一个 Taskflow

executor提供了几种线程安全的方法来运行任务流。

// runs the taskflow once
tf::Future<void> run_once = executor.run(taskflow); 

// wait on this run to finish
run_once.get();

// run the taskflow four times
executor.run_n(taskflow, 4);

// runs the taskflow five times
executor.run_until(taskflow, [counter=5](){ return --counter == 0; });

// block the executor until all submitted taskflows complete
executor.wait_for_all();

可视化图结构

使用dump,生成定义好的结构图,生成的内容复制到下面的网站:
GraphViz Online

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

    tf::Task A = taskflow.emplace([] () {}).name("A");
    tf::Task B = taskflow.emplace([] () {}).name("B");
    tf::Task C = taskflow.emplace([] () {}).name("C");
    tf::Task D = taskflow.emplace([] () {}).name("D");
    tf::Task E = taskflow.emplace([] () {}).name("E");
    A.precede(B, C, E);
    C.precede(D);
    B.precede(D, E);

    // dump the graph to a DOT file through std::cout
    taskflow.dump(std::cout); 

    return 0;
}
digraph Taskflow {
subgraph cluster_p0xffffd542ef38 {
label="Taskflow: p0xffffd542eee0";
p0xffffa6914830[label="A" ];
p0xffffa6914830 -> p0xffffa6914928;
p0xffffa6914830 -> p0xffffa6914a20;
p0xffffa6914830 -> p0xffffa6914c10;
p0xffffa6914928[label="B" ];
p0xffffa6914928 -> p0xffffa6914b18;
p0xffffa6914928 -> p0xffffa6914c10;
p0xffffa6914a20[label="C" ];
p0xffffa6914a20 -> p0xffffa6914b18;
p0xffffa6914b18[label="D" ];
p0xffffa6914c10[label="E" ];
}
}

在这里插入图片描述

标签:executor,emplace,使用,taskflow,put,Task,简单,tf,Taskflow
From: https://blog.csdn.net/Jj147258369/article/details/137150819

相关文章

  • python 脚本对数据库的简单操作
    importsqlite3fromdatetimeimportdatetime'''数据库内容[ID]intnull,[loginName]text(50),[loginTime]text(50),[logOutTime]text(50),[operation]intnull'''#连接到数据库conn=sqlite3.connect('test.......
  • 使用OpenCV实现换脸
    使用OpenCV实现换脸换脸介绍算法原理与流程效果程序换脸介绍换脸技术,顾名思义,是一种在不改变原始人物的基本特征,如发型、脸颊轮廓等前提下,巧妙地将该人物的五官特征替换为另一人的五官特征的技术。算法原理与流程易容术算法的关键步骤在于精准定位图像中的人脸位置......
  • NO12 蓝桥杯单片机之DS1302的使用
    1DS1302是什么DS1302由两块存储器组成,一个是日历时钟寄存器还有一个是31位的静态RAM存储器。而在蓝桥杯中常考的就是日历时钟寄存器,故这里只介绍日历时钟寄存器。简单来说,其就是一个“电子表”,他会自动的实时记录时间,而不需要像我们之前运用定时器做的时钟一样,要自己来设计......
  • 简单介绍c语言程序的编译与链接
    程序运行的背后程序在运行时经历了四个步骤,分别是预编译(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。预编译预编译也称预处理,源代码文件(.c)和相关的头文件(.h)等被预编译器cpp预编译成一个.i文件。编译编译过程就是将预处理后的文件进行一系列......
  • Python Numpy第三方库的基本使用
    1.下载Numpy第三方库pipinstallnumpy2.导入第三方库importnumpyasnp3.一些基本操作importnumpyasnpnum1=np.array([1,2,3,4,5])#创建数组print(num1)num2=np.zeros((3,2))#创建全零数组print(num2)print(num2.shape)#打印数组尺寸num3=np.ones((2,4))#创建......
  • 使用NSQ
    nsq最初是由bitly公司开源出来的一款简单易用的消息中间件,它可用于大规模系统中的实时消息服务,并且每天能够处理数亿级别的消息。它有以下特性:分布式。它提供了分布式的、去中心化且没有单点故障的拓扑结构,稳定的消息传输发布保障,能够具有高容错和高可用特性。易于扩展。它支......
  • 【GitLab】Ubuntu使用宝塔安装GitLab最新社区版
    首先在Ubuntu安装宝塔面板在官网可以找到脚本一键安装安装GitLab社区版然后在宝塔面板的“软件商店”里面找到GitLab最新社区版12.8.1一键安装安装过程中可能出现以下问题:1.卡在ruby_block[waitforlogrotateservicesocket]actionrun解决办法:在Ubuntu终端中运行......
  • KingbaseES 索引unusable的使用
    前言KingbaseES中,索引不可用原则介绍oracle数据库中,如果索引不可用(unusable),在进行DML操作时,会触发报错:索引不可用。在KES中如果设置索引不可用,插入数据不会报错,因为当索引被置为不可用状态后,如果要重新启动索引需要rebuild索引,此时会在新索引中重新组织表tuple数据。insert......
  • LangChain SQL介绍以及使用Qwen1.5执行SQL查询教程
    该模块可以让我们向LLM提问时从数据库中查询数据并做出回答。架构SQLchain和agent的高层抽象架构:问题转查询SQL:使用LLM将用户输入转成SQL查询执行SQL查询:执行SQL语句回答问题:LLM根据数据库查询结果返回回答内容环境安装安装必要环境和包pipinstall--upgrade--quiet......
  • WPF中使用PDF模板实现PDF导出和预览-来自GPT4
    在C#和WPF项目中实现加载不同的PDF模板、查看报告和导出PDF文件的功能,可以通过以下步骤完成:1.选择PDF库首先,选择一个合适的.NETPDF库。有许多库可以帮助你处理PDF文件,包括但不限于:iTextSharp:一个功能强大的和灵活的库,适用于创建和修改PDF文件。它是iText的一个.NET端口。......