大家好,我是不会写代码的纯序员——Chunel。
在写程序的过程中,大家经常会遇到一些条件判断逻辑。就拿我来说吧,多年的工作经历,已经让我熟练掌握的通过编程来实现分支判断逻辑。代码如下:
if (true) {
printf("yes");
} else {
printf("输出啥已经不重要了");
}
还有,工作中可能还会遇到一些不太确定的选择。我举个例子(咳咳,注意了,开始上路了)哈:一个工作流程(pipeline)中包含了a、b、c三个流程,我们执行这个流程的时候,需要根据“某个/某些条件”从中选择一个执行。这个条件,可能跟当前的pipeline
完全没有关系;也可能和当前pipeline
之前的一些内容有关;还有可能跟这个pipeline
的历史有关。
今天,我们主要来说一下,CGraph框架是如何在多个算子中,通过condition实现条件判断功能的。提前声明一下,看这一篇内容之前,需要把上一篇 《纯序员给你介绍图化框架的简单实现——参数传递》 的内容了解一下。
// 首先,std::cout << "源码链接 : https://github.com/ChunelFeng/CGraph " << std::endl;
功能介绍 |
我们再来看一下下面这张图哈:
主要看一下condition模块上下游的一条关系链:element->group->condition->function condition
。其中,function condition
中,又包含了function node-1/2/3 三个功能节点。
解释一下,condition
继承自group
模块,就说明了它本身是支持插入多个function node
的。function condition
模块,又继承自condition
,是具体的实现模块。
tips:图中虚线框模块(如
group
),表示不可执行部分;实线框模块(如region
),表示可执行部分;点虚线框模块,表示可能执行部分(如function condition
中的function node-1
)
同时,function condition
还和param
模块相连,表名了它也可以获取和修改param
模块中的内容——这就是我为什么让你先看一下上一篇文章的原因,哈哈。
具体实现 |
1、不依赖当前pipeline:
class MyCondition : public GCondition {
public:
int choose () override {
return 1;
}
};
void tutorial_condition() {
/* 代码参考CGraph中/tutorial/T06-Condition.cpp */
CSTATUS status = STATUS_OK;
GPipelinePtr pipeline = GPipelineFactory::create();
GElementPtr a, b_condition, c, d_condition = nullptr;
b_condition = pipeline->createGGroup<MyCondition>({
pipeline->createGNode<MyNode1>(GNodeInfo("conditionNodeB0", 1)),
pipeline->createGNode<MyNode2>(GNodeInfo("conditionNodeB1", 1)),
pipeline->createGNode<MyNode1>(GNodeInfo("conditionNodeB2", 1))
}); // 生成 b_condition。执行的时候,根据choose()的返回值,在B0,B1,B2中选择一个执行
d_condition = pipeline->createGGroup<MyParamCondition>({
pipeline->createGNode<MyNode1>(GNodeInfo("paramConditionNodeD0", 1)),
pipeline->createGNode<MyNode1>(GNodeInfo("paramConditionNodeD1", 1)),
pipeline->createGNode<MyNode1>(GNodeInfo("paramConditionNodeD2", 1))
});
if (nullptr == b_condition || nullptr == d_condition) {
return;
}
status = pipeline->registerGElement<MyWriteParamNode>(&a, {}, "writeNodeA", 1);
status = pipeline->registerGElement<MyCondition>(&b_condition, {a}, "conditionB", 1);
status = pipeline->registerGElement<MyReadParamNode>(&c, {b_condition}, "readNodeC", 1);
status = pipeline->registerGElement<MyParamCondition>(&d_condition, {c}, "conditionD", 1);
status = pipeline->init();
for (int i = 0; i < 3; i++) {
status = pipeline->run();
std::cout << "[CGraph] tutorial_condition, loop : " << i + 1 << ", and run status = " << status << std::endl;
}
status = pipeline->deinit();
GPipelineFactory::destroy(pipeline);
}
我们先来看一下不依赖当前pipeline
的例子。上面的例子简单吧——自己实现了一个类(MyCondition),继承自GCondition,并且实现了父类的choose方法(纯虚方法)。它的意思就是,每次执行到这里的时候,仅执行MyCondition类中的第1个(从0开始计算,专业不)算子。
用上面那段代码来描述,就是在b_condition这个算子中,包含了conditionNodeB0/conditionNodeB1/conditionNodeB2 三个算子,注册进入pipeline
后,每次执行到这里的时候,都会仅执行conditionNodeB1,因为choose()方法返回值固定是1啊。
嗯,这个例子好像也没啥实际的使用场景哈。这样吧,你可以这样想,注册6个节点,然后choose方法里,return rand() % 6
,这样就当掷骰子了哈。
2、 依赖当前pipeline:
再看一个跟pipeline
内容有关的哈。
class MyParamCondition : public GCondition {
public:
int choose () override {
MyParam* myParam = this->getGParam<MyParam>("param1");
if (nullptr == myParam) {
return GROUP_LAST_ELEMENT_INDEX;
}
int cnt = myParam->iCount;
return cnt;
}
};
MyParamCondition这个类也继承自GCondition类。与刚才不同的是,这个类的choose()函数中通过getGParam方法,获取了pipeline
中参数名为"param1"的"myParam"类型的参数,根据其中的iCount值,来判断需要执行第几个算子。如果pipeline
中,没有这个参数的话,则会默认执行当前condtion
中的最后一个算子——当然了,这个是属于自定义逻辑,你也可以让它执行第一个算子啊。
这样看来,上游的算子就可以通过修改特定参数的值,来控制下游condition
模块的执行逻辑了。换句话说,就是condition
模块的执行逻辑,可以根据上游逻辑来决定。
需要强调的是,通过这种方式获取参数,同样需要考虑与上下游链路的情况。有并发写入的情况下,还是要做好参数的加锁保护的。
3、依赖pipeline历史执行情况:
其实,理解了上面一种情况,这种情况也就比较好理解了。之前的文章中说过,参数是可以在pipeline
不同的执行轮数中传递的。可以设定,当前pipeline
执行结束后,是否将参数中特定值进行复位。
依赖pipeline
的历史运行情况,只需要结合param
不复位的逻辑,就可以很轻松实现了。这里不做过多说明。反正懂的都懂,不懂的可以直接加我微信鸭,哈哈。
一些说明 |
除了上面的使用方法之外,再简单讲一些说明吧。
比如,想知道当前condition
模块中有多少个算子,可以通过getRange()
来获取。这个功能在开发的过程中可能会被用到。
condition
模块最终也继承自element
模块,这个说明了它自身也可以和其他任何模块随机组合,比如 region
中加入condition
,又或者condition
中加入cluster
模块。
还有一点,就是现在的设定是,如果当前condition
中有n个算子,而choose方法返回的值大于n的话,则不执行任何一个算子,且没有任何异常提示。直接执行下面的逻辑。至于说为什么,主要原因是:我觉得这样好,嘿嘿。
写在最后 |
我们在日常开发的过程中,除了ctrl+c/v之外,最常用的其实就是if/else。这里主要跟大家描述了CGraph中条件判断逻辑,配合上参数传递和循环执行的逻辑,可以实现非常多的功能。你想啊,平日里我们写的程序,其实不就是for/if/else的各种组合么。而CGraph在做的,无非就是把这些逻辑,移植到一个图框架中来了。
在下一章中,我们会给大家介绍一下,CGraph在处理需要并行的任务的时候,是如何进行线程调度优化的。顺便提一句,CGraph自身也可以被当做是一个简单易用且性能很好的线程池来使用哦。
如果您对这些内容感兴趣,也很欢迎加入我们,一起去构建和优化CGraph,或者是基于CGraph框架进行一些属于自己的功能开发。再或者,给我们提点意见或者bug那也是极好的。还是那句话,Build together, power another.
[2021.07.25 by Chunel]
个人信息
微信: ChunelFeng
邮箱: chunel@foxmail.com
个人网站:www.chunel.cn
github地址: https://github.com/ChunelFeng