今天,想聊聊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
发布评论请先 登录
老生常谈---一种裸奔多任务模型
裸奔环境下的多任务模型
allegro中workflow manager求解
OPC 实时任务系统动态调度算法的研究与设计The Stud
一种基于角色和任务的访问控制模型
控制系统中实时任务分析
基于页的8051多任务模型

基于改进蜂群算法的多维QoS云计算任务调度算法
嵌入式多核处理器任务调度研究

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

模型任务的评价指标体系
workflow异步任务调度编程范式


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

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

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

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

米尔基于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、

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

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

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

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

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

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

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

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

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

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