0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

workflow的任务模型

汽车电子技术 来源: 程序喵大人 作者: 程序喵大人 2023-02-21 14:05 1082次阅读

今天,想聊聊workflow这个开源项目。

关于workflow,我之前特意写过一篇文章【推荐学习这个C++开源项目】。

今天还是想再啰嗦啰嗦,因为自己这一年也在带团队从0到1做项目,需要负责整个项目的架构设计、接口设计、模块划分等工作。

做了一段时间后再回过头复盘一下,深知架构设计、接口设计的重要性,也感受到了架构设计的困难程度,编码和设计相比,真的容易的多了。

然后自己又回头来研究了一下workflow,想着学习下其他项目的设计理念,随着自己研究的越来越深入,越来越感觉它的高端,对外暴露特别简单的接口却能完成非常复杂的功能。

上篇文章是基础篇,主要向大家普及一下workflow的特点和作用,感兴趣的朋友可以移步到那里哈。

本篇文章是进阶篇,主要就是想介绍下workflow的任务模型,其他的框架一般只能处理普通的网络通信,而workflow却特别适用于通信与计算关系很复杂的应用。其实我最感兴趣的是它的内存管理机制,下面也会详细介绍。

图片

图片

优秀的系统设计

图片

图片

在作者的设计理念中:程序 = 协议 + 算法 + 任务流。

**协议:**就是指通用的网络协议,比如http、redis等,当然还可以自定义网络协议,这里只需要提供序列化和反序列化函数就可以达到想要的效果。

算法: workflow提供了一些通用的算法,比如sort、merge、reduce等,当然还可以自定义算法,用过C++ STL的朋友应该都知道如何自定义算法吧,在workflow中,任何复杂的计算都应该包装成算法。

**任务流:**我认为这应该就是整个系统设计的核心,通过任务流来抽象封装实际的业务逻辑,就是把开发好的协议和算法组成一个任务流程图,然后调度执行这个图。

图片

图片

任务流

图片

图片

这里多聊聊任务流,在workflow中,一切业务逻辑皆是任务,多个任务会组成任务流,任务流可组成图,这个图可能是串联图,可能是并联图,也有可能是串并联图,类似于这种:

图片

也可能是这种复杂的DAG图:

图片

图的层次结构可以由用户自定义,框架也是支持动态地创建任务流。

引用作者的一句话:

图片

图片

如果把业务逻辑想象成用设计好的电子元件搭建电路,那么每个电子元件内部可能又是一个复杂电路。

workflow对任务做了很好的抽象和封装。整个系统包含6种基础任务:通讯、文件IO、CPU定时器、计数器。

workflow提供了任务工厂,所有的任务都由任务工厂产生,并且会自动回收。

大多数情况下,通过任务工厂创建的任务都是一个复合任务,但用户并不感知。例如一个http请求,可能包含很多次异步过程(DNS,重定向),内部有很多复杂的任务,但对用户来讲,这就是一次简单的通信任务。

哪有什么岁月静好,只不过是有人替你负重前行。workflow的一大特点就是接口暴露的特别简洁,非常方便用户的接入,外部接入如此简单,肯定是将很多组合的逻辑都放在了内部,但其实workflow项目内部代码结构层次非常简洁清晰,感兴趣的朋友可以自己研究研究哈。

图片

图片

内存管理机制

图片

图片

还有就是项目的内存管理机制,workflow整个项目都遵循着谁申请谁释放的原则,内部申请的内存不需要外部显式释放,内部会自动回收内存。

而且整个项目都没有使用shared_ptr,那多个对象共同使用同一块裸内存,workflow是怎么处理的呢?

内部为这种需要共享的对象各自维护一个引用计数,手动incref和decref,至于为什么要这样做,可以看看workflow美女架构师的回答【https://www.zhihu.com/question/33084543/answer/2209929271】。

我总结了一下:

  • shared_ptr是非侵入式指针,一层包一层,而且为了保持shared_ptr覆盖对象整个生命周期,每次传递时都必须带着智能指针模板,使用具有传染性,而且也比较麻烦。
  • shared_ptr引用计数的内存区域和数据区域不一致,不连续,缓存失效可能导致性能问题,尽管有make_shared,但还是容易用错。
  • 手动为对象做incref和decref,使用起来更灵活,可以明确在引用计数为固定数字时做一些自定义操作,而且方便调试。因为手动管理对象的引用计数,就会要求开发者明晰对象的生命周期,明确什么时候该使用对象,什么时候该释放对象。
  • 如果使用shared_ptr可能会激起开发者的惰性,反正也不需要管理内存啦,就无脑使用shared_ptr呗,最后出现问题时调试起来也比较困难。

那再深入源码中研究研究,看看workflow是如何做到把对象指针给到外部后,内部还自动回收的。

拿WFClientTask举例说明一下,workflow中所有的Task都是通过Factory创建:

static WFHttpTask *create_http_task(const std::string& url,
                    int redirect_max,
                    int retry_max,
                    http_callback_t callback);


using WFHttpTask = WFNetworkTask;

template <class REQ, class RESP>
class WFClientTask : public WFNetworkTask {};

注意,create参数里有一个callback,workflow一定会执行这个callback,然后内部回收掉该WFClientTask占用的内存,任何任务的生命周期都是从创建到callback函数结束。

它是怎么做到的?继续看下WFClientTask的继承层次结构:

template <class REQ, class RESP>
class WFClientTask : public WFNetworkTask {};


template<class REQ, class RESP>
class WFNetworkTask : public CommRequest {};


class CommRequest : public SubTask, public CommSession {};


class SubTask {
public:
  virtual void dispatch() = 0;
private:
  virtual SubTask *done() = 0;
protected:
  void subtask_done();
};

WFClientTask继承于WFNetworkTask,WFNetworkTask又继承于SubTask。

SubTask内部有subtask_done()方法,看下它的实现:

void SubTask::subtask_done() {
  SubTask *cur = this;
  ParallelTask *parent;
  SubTask **entry;


  while (1) {
    parent = cur->parent;
    entry = cur->entry;
    cur = cur->done();
    if (cur) {
      cur->parent = parent;
      cur->entry = entry;
      if (parent)
        *entry = cur;
      cur->dispatch();
    }
    else if (parent) {
      if (__sync_sub_and_fetch(&parent->nleft, 1) == 0) {
        cur = parent;
        continue;
      }
    }
    break;
  }
}

subtask_done()方法中会调用它的done()方法,然而这几个方法都是virtual方法,看看WFClientTask是怎么重写它们的:

template <class REQ, class RESP>
class WFClientTask : public WFNetworkTask<REQ, RESP> {
protected:
  virtual SubTask *done() {
    SeriesWork *series = series_of(this);
    if (this->state == WFT_STATE_SYS_ERROR && this->error < 0) {
      this->state = WFT_STATE_SSL_ERROR;
      this->error = -this->error;
    }
    if (this->callback)
      this->callback(this);
    delete this;
    return series->pop();
  }
};

子类重写了done()方法,可以看到在它的实现里,触发了callback,然后调用了delete this,释放掉了当前占用的这块内存。

那谁调用的done(),可以看下上面的代码,subtask_done()会触发done(),那谁触发的subtask_done():

void CommRequest::dispatch() {
  if (this->scheduler->request(this, this->object, this->wait_timeout,
                 &this->target) < 0) {
    this->state = CS_STATE_ERROR;
    this->error = errno;
    if (errno != ETIMEDOUT)
      this->timeout_reason = TOR_NOT_TIMEOUT;
    else
      this->timeout_reason = TOR_WAIT_TIMEOUT;
    this->subtask_done();
  }
}

可以看到,dispatch()里触发了subtask_done(),那谁触发的dispatch():

template<class REQ, class RESP>
class WFNetworkTask : public CommRequest {
public:
  /* start(), dismiss() are for client tasks only. */
  void start() {
    assert(!series_of(this));
    Workflow::start_series_work(this, nullptr);
  }
};


inline void
Workflow::start_series_work(SubTask *first, SubTask *last,
              series_callback_t callback) {
  SeriesWork *series = new SeriesWork(first, std::move(callback));
  series->set_last_task(last);
  first->dispatch();
}

这里可以看到,Task的start()方法触发start_series_work(),进而触发dispatch()方法。

总结一下:

● 步骤一

通过工厂方法创建WFClientTask,同时设置callback;

● 步骤二

外部调用start()方法,start()中调用Workflow::start_series_work()方法;

● 步骤三

start_series_work()中调用SubTask的dispatch()方法,这个dispatch()方法由SubTask的子类CommRequest(WFClientTask的父类)实现;

● 步骤四

dispatch()方法在异步操作结束后会触发subtask_done()方法;

● 步骤五

subtask_done()方法内会触发done()方法;

● 步骤六

done()方法内会触发callback,然后delete this;

● 步骤七

内存释放完成。

其实这块可以猜到,想要销毁自己的内存,基本上也就delete this这个方法。

然而我认为这中间调用的思想和过程才是我们需要重点关注和学习的,远不止我上面描述的这么简单,感兴趣的朋友可以自己去研究研究哈。

关于workflow还有很多优点,这里就不一一列举啦,它的源码也值得我们CPP开发者学习和进阶,具体可以看我之前的文章。

发现workflow团队对这个项目相当重视,还特意建了个QQ交流群(群号码是618773193),对此项目有任何问题都可以在这个群里探讨,也方便了我们学习,真的不错。项目地址如下:https://github.com/sogou/workflow,也可以点击阅读原文直达。

在访问GitHub遇到困难时,可使用他们的Gitee官方仓库:https://gitee.com/sogou/workflow。

项目也特别实用,据说搜狗内外很多团队都在使用workflow。感觉这个项目值得学习的话就给人家个star,不要白嫖哈,对项目团队来说也是一种认可和鼓励。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 框架
    +关注

    关注

    0

    文章

    404

    浏览量

    17753
  • 网络通信
    +关注

    关注

    4

    文章

    824

    浏览量

    30673
  • workflows
    +关注

    关注

    0

    文章

    6

    浏览量

    5992
收藏 人收藏
    相关推荐
    热点推荐

    老生常谈---一种裸奔多任务模型

    一种裸奔多任务模型一个网友的总结:stateMachine + timerTick + queue。在RTOS环境下的多任务模型任务通常阻塞在一个OS调用上(比如从消息队列取数据)。外部如果想让该
    发表于 12-08 10:13

    裸奔环境下的多任务模型

    对于简单的嵌入式应用多数裸奔就能解决,但写出来的裸奔代码质量也由好坏之分。在网上看到了这样一篇文字:上面说到了裸奔环境下的多任务模型 - stateMachine + timerTick
    发表于 01-21 07:41

    allegro中workflow manager求解

    allegro中analysis菜单下使用workflow manager,点击start analysis后显示implement analysis failed或者coupling analysis failed?
    发表于 03-07 22:02

    Stage模型深入解读

    基于Stage模型开发应用,下面将会从应用组件、进程模型、线程模型任务模型、后台运行机制、应用配置文件6个方面进行介绍。 1、组件模型
    发表于 03-15 10:32

    OPC 实时任务系统动态调度算法的研究与设计The Stud

    本文基于已有的OPC Server 实时任务模型,设计了处理混合任务集的动态调度算法(基于截止期优先)和实现方式。该算法实现了对混合任集可调度性的判断,可以完成有硬实时性要
    发表于 05-31 15:36 13次下载

    一种基于角色和任务的访问控制模型

    在基于角色的访问控制模型的基础上引入了任务(task)和任务实例(task instance)的概念,建立了基于角色和任务的访问控制模型(R
    发表于 08-05 16:30 8次下载

    控制系统中实时任务分析

    本文分析了控制系统任务的特点,给出了控制系统中各种实时任务模型。分析了控制系统性能与任务参数之间的关系,给出了参数的设置方法。最后,研究了控制系统中实时任务
    发表于 08-06 08:35 10次下载

    基于页的8051多任务模型

      随着8051微控制器性能的不断提高,使用多任务操作系统对单片机进行资源管理已成为当代开发的需要。由于受静态链接的限制,8051系统的多任务开发需要处理代码重入(reentran
    发表于 09-25 17:34 1049次阅读
    基于页的8051多<b class='flag-5'>任务模型</b>

    基于改进蜂群算法的多维QoS云计算任务调度算法

    针对云计算环境下用户日益多样化的QoS需求和高效的资源调度要求,提出了基于改进蜂群算法的多维QoS云计算任务调度算法,其中包括构建任务模型、云资源模型和用户QoS模型。为了获得高效的调
    发表于 12-01 16:11 0次下载

    嵌入式多核处理器任务调度研究

    任务模型而选择粒子群算法的适应度函数,综合利用局部最优极值和全局最优极值的优势,优化了粒子群算法中存在的过早收敛问题,使算法具有较高的收敛效率。实验结果表明,与基于遗传算法的多核多线程任务调度算法相比,该算法
    发表于 01-17 17:49 1次下载
    嵌入式多核处理器<b class='flag-5'>任务</b>调度研究

    一种基于神经网络的联合识别方法

    从事件时序关系与因果关系的关联性出发,提出基于神经网络的联合识别方法。将时序关系和因果关系识别分别作为主任务和辅助任务,设计共享辅助任务中编码层、解码层和编解码层的3种联合识别模型,通
    发表于 03-25 15:47 11次下载
    一种基于神经网络的联合识别方法

    开源软件-Sogou C++ Workflow高性能C++服务器引擎

    ./oschina_soft/gitee-workflow.zip
    发表于 06-20 09:36 1次下载
    开源软件-Sogou C++ <b class='flag-5'>Workflow</b>高性能C++服务器引擎

    模型任务的评价指标体系

    1. 写在前面 模型“好”与“坏”的评价指标直接由业务目标/任务需求决定。我们需要做的是:根据具体的业务目标/任务需求去选择相应的评价指标,继而选出符合业务目标/任务需求的好
    的头像 发表于 01-11 10:10 1028次阅读

    基于M55H的定制化backbone模型AxeraSpine

    Backbone模型是各种视觉任务训练的基石,视觉任务模型的性能和模型的速度都受backbone模型的影响
    的头像 发表于 10-10 16:09 1208次阅读
    基于M55H的定制化backbone<b class='flag-5'>模型</b>AxeraSpine

    workflow异步任务调度编程范式

    workflow是一个异步任务调度编程范式,封装了6种异步资源:CPU计算、GPU计算、网络、磁盘I/O、定时器、计数器,以回调函数模式提供给用户使用,概括起来实际上主要是两个功能:1、屏蔽阻塞调用的影响,使阻塞调用的开发接口变为异步的,充分利用计算资
    的头像 发表于 11-09 09:42 717次阅读
    <b class='flag-5'>workflow</b>异步<b class='flag-5'>任务</b>调度编程范式

    半导体芯片需要做哪些测试

    首先我们需要了解芯片制造环节做⼀款芯片最基本的环节是设计->流片->封装->测试,芯片成本构成⼀般为人力成本20%,流片40%,封装35%,测试5%(对于先进工艺,流片成本可能超过60%)。测试其实是芯片各个环节中最“便宜”的一步,在这个每家公司都喊着“CostDown”的激烈市场中,人力成本逐年攀升,晶圆厂和封装厂都在乙方市场中“叱咤风云”,唯独只有测试显

    汉通达
    22小时前
    327

    解决方案 | 芯佰微赋能示波器:高速ADC、USB控制器和RS232芯片——高性能示波器的秘密武器!

    示波器解决方案总述:示波器是电子技术领域中不可或缺的精密测量仪器,通过直观的波形显示,将电信号随时间的变化转化为可视化图形,使复杂的电子现象变得清晰易懂。无论是在科研探索、工业检测还是通信领域,示波器都发挥着不可替代的作用,帮助工程师和技术人员深入剖析电信号的细节,精准定位问题所在,为创新与发展提供坚实的技术支撑。一、技术瓶颈亟待突破性能指标受限:受模拟前端

    芯佰微电子
    21小时前
    411

    硬件设计基础----运算放大器

    1什么是运算放大器运算放大器(运放)用于调节和放大模拟信号,运放是一个内含多级放大电路的集成器件,如图所示:左图为同相位,Vn端接地或稳定的电平,Vp端电平上升,则输出端Vo电平上升,Vp端电平下降,则输出端Vo电平下降;右图为反相位,Vp端接地或稳定的电平,Vn端电平上升,则输出端Vo电平下降,Vn端电平下降,则输出端Vo电平上升2运算放大器的性质理想运算

    张飞实战电子官方
    1天前
    315

    ElfBoard技术贴|如何调整eMMC存储分区

    ELF 2开发板基于瑞芯微RK3588高性能处理器设计,拥有四核ARM Cortex-A76与四核ARM Cortex-A55的CPU架构,主频高达2.4GHz,内置6TOPS算力的NPU,这一设计让它能够轻松驾驭多种深度学习框架,高效处理各类复杂的AI任务。

    ElfBoard
    1天前
    605

    米尔基于MYD-YG2LX系统启动时间优化应用笔记

    1.概述MYD-YG2LX采用瑞萨RZ/G2L作为核心处理器,该处理器搭载双核Cortex-A55@1.2GHz+Cortex-M33@200MHz处理器,其内部集成高性能3D加速引擎Mail-G31GPU(500MHz)和视频处理单元(支持H.264硬件编解码),16位的DDR4-1600/DDR3L-1333内存控制器、千兆以太网控制器、USB、CAN、

    米尔电子
    2天前
    286

    运放技术——基本电路分析

    虚短和虚断的概念由于运放的电压放大倍数很大,一般通用型运算放大器的开环电压放大倍数都在80dB以上。而运放的输出电压是有限的,一般在10V~14V。因此运放的差模输入电压不足1mV,两输入端近似等电位,相当于“短路”。开环电压放大倍数越大,两输入端的电位越接近相等。“虚短”是指在分析运算放大器处于线性状态时,可把两输入端视为等电位,这一特性称为虚假短路,简称

    张飞实战电子官方
    2天前
    383

    飞凌嵌入式携手中移物联,谱写全国产化方案新生态

    4月22日,飞凌嵌入式“2025嵌入式及边缘AI技术论坛”在深圳成功举办。中移物联网有限公司(以下简称“中移物联”)携OneOS操作系统与飞凌嵌入式共同推出的工业级核心板亮相会议展区,操作系统产品部高级专家严镭受邀作《OneOS工业操作系统——助力国产化智能制造》主题演讲。

    飞凌嵌入式
    2天前
    970

    ATA-2022B高压放大器在螺栓松动检测中的应用

    实验名称:ATA-2022B高压放大器在螺栓松动检测中的应用实验方向:超声检测实验设备:ATA-2022B高压放大器、函数信号发生器,压电陶瓷片,数据采集卡,示波器,PC等实验内容:本研究基于振动声调制的螺栓松动检测方法,其中低频泵浦波采用单频信号,而高频探测波采用扫频信号,利用泵浦波和探测波在接触面的振动声调制响应对螺栓的松动程度进行检测。通过螺栓松动检测

    Aigtek安泰电子
    05-06 18:44
    1.1k

    MOS管驱动电路——电机干扰与防护处理

    此电路分主电路(完成功能)和保护功能电路。MOS管驱动相关知识:1、跟双极性晶体管相比,一般认为使MOS管导通不需要电流,只要GS电压(Vbe类似)高于一定的值,就可以了。MOS管和晶体管向比较c,b,e—–>d(漏),g(栅),s(源)。2、NMOS的特性,Vgs大于一定的值就会导通,适合用于源极接地时的情况(低端驱动),只要栅极电压达到4V或10V就可以

    张飞实战电子官方
    05-06 19:34
    407

    压敏(MOV)在电机上的应用剖析

    一前言有刷直流电机是一种较为常见的直流电机。它的主要特点包括:1.结构相对简单,由定子、转子、电刷和换向器等组成;2.通过电刷与换向器的接触来实现电流的换向,从而使电枢绕组中的电流方向周期性改变,保证电机持续运转;3.具有调速性能较好等优点,可以通过改变电压等方式较为方便地调节转速。有刷直流电机在许多领域都有应用,比如一些电动工具、玩具、小型机械等。但它也存

    深圳市韬略科技有限公司
    05-06 11:34
    282

    硬件原理图学习笔记

    这一个星期认真学习了硬件原理图的知识,做了一些笔记,方便以后查找。硬件原理图分为三类1.管脚类(gpio)和门电路类输入输出引脚,上拉电阻,三极管与门,或门,非门上拉电阻:正向标志作用,给悬空的引脚一个确定的状态三极管:反向三极管(gpio输出高电平,NP两端导通,被控制端导通,电压为0)->NPN正向三极管(gpio输出低电平,PN两端导通,被控制端导通,

    张飞实战电子官方
    04-30 18:40
    474

    TurMass™ vs LoRa:无线通讯模块的革命性突破

    TurMass™凭借其高传输速率、强大并发能力、双向传输、超强抗干扰能力、超远传输距离、全国产技术、灵活组网方案以及便捷开发等八大优势,在无线通讯领域展现出强大的竞争力。

    道生物联
    05-06 10:50
    940

    RZT2H CR52双核BOOT流程和例程代码分析

    RZT2H是多核处理器,启动时,需要一个“主核”先启动,然后主核根据规则,加载和启动其他内核。本文以T2H内部的CR52双核为例,说明T2H多核启动流程。

    RA生态工作室
    04-03 17:14
    2.1k

    干簧继电器在RF信号衰减中的应用与优势

    在电子测试领域,RF(射频)评估是不可或缺的一部分。无论是研发阶段的性能测试,还是生产环节的质量检测,RF测试设备都扮演着关键角色。然而,要实现精准的RF评估,测试设备需要一种特殊的电路——衰减电路。这些电路的作用是调整RF信号的强度,以便测试设备能够准确地评估RF组件和RF电路的各个方面。衰减器的挑战衰减器的核心功能是校准RF信号的强度。为了实现这一点,衰

    斯丹麦德电子
    04-30 11:33
    748

    ElfBoard嵌入式教育科普|ADC接口全面解析

    当代信息技术体系中,嵌入式系统接口作为数据交互的核心基础设施,构成了设备互联的神经中枢。基于标准化通信协议与接口规范的技术架构,实现了异构设备间的高效数据交换与智能化协同作业。本文选取模数转换接口ADC作为技术解析切入点,通过系统阐释其工作机理、性能特征及重要参数,为嵌入式学习者爱好者构建全维度接口技术认知框架。

    ElfBoard
    04-30 09:34
    428