[C/C++]状态机

方法:

  • 1.用于接收事件,并根据事件类型到相关的表中分配处理函数 所以只需要把事件类型(通常是消息),传入状态机进行分配即可

  • 2.通常只是读取的一些操作,不涉及更改的,只是读取显示的,都放在预处理中处理(就是和状态无关)

  • 3.状态机编程的一些基础原则: 1.除了到初始化状态和退出状态,其他所有的状态,之前的迁移,必须保证每个状态有且仅有一个消息能够使得状态发生转移所以画状态转移图的时候,正常的话应该都遵从这个原则,不能有第二个消息。

  • 4.状态机的处理逻辑: 收到消息后,先处理这条外部消息{任何消息,先到预处理表中查看是否需要处理,然后查看是否需要进入主处理(分两步,①主处理,②检查是否开启了下一层状态机,再次检查一遍这条消息是否要处理),接着检查是否退出这层状态机的操作(栈深度) 如果退出,且主处理处理成功,则直接返回,优先处理状态机退出的结果消息再处理缓存(就不进行后处理了),如果主处理处理成功,且当前发生了退栈操作,则检查是否需要处理缓存,需要的话就处理, 之所以退栈才处理缓存是因为只要子状态机在运行就表示之前触发这个子状态机的那条消息还没有全部处理完毕,如果主处理表示处理失败,或者是没有找到这条消息,进入后处理进行处理(里面可能会设置一些内部消息)},处理内部消息队列

  • 内部消息:就是状态机内部,自己模块内发送的消息,一般用于main状态机向当前层二状态机发送处理请求,在层二状态机中进行处理

  • 4.1状态机的状态和if的取舍(加全局变量或者加状态):状态机的状态本质上也是一种if的条件,其实更像switch分支,而每个状态里面收到的不同消息其实也是一个if分支,所以状态机本质上就是2层
    的if分支。 之所以用状态机,不用switch(一些旧代码部分还是用switch的),是因为状态机可读性更好,框架感更好(或者说在多人编程中,可以作为一种约束,保证整体结构是符合要求的),将前面
    的分支和当前的处理剥离,减少了 不必要的语句显示,所以可读性就更好了 什么条件该作为状态,什么只作为状态机处理函数中的一个if分支呢?状态就像是树干(想象一下所有状态其实都是switch,即都有
    一个共性,或者都属于一类的),这样才能提升可读性(可以理解为状态机状态是模拟面向对象 的编程中对象可能的不同的实际形态)。当状态机的处理函数很复杂,包含很多可能性的时候,会在启动一个状态机,就是
    层二状态机。简单而言,一层状态就是从一个角度,对某个事物进行分类处理

  • 4.2状态机中的消息是有默认缓存机制的,比如一条消息过来了,正在执行代码的过程中(0.几秒),这个时候,又来一个消息时不会马上执行的,至少需要等上一个消息执行完毕在执行-即常说的消息队列
    这个和自定义的缓存机制不同,后者是消息进去执行代码了,在代码里面再进行缓存。所以在sd阶段考虑时序问题的时候,这个代码中来消息的场景可以忽略的。
    目前状态机的机制是:先预处理,然后处理实际的消息,接着处理内部队列消息,如果发生退出状态机行为,再处理缓存消息,最后再是后处理,有一些场景可能缓存消息无法适用,比如XSD在退出层二状态机,处理内
    部队列消息的时候 又发起了搜网,这个时候,缓存消息就不会被触发了。这种情况在SD的时候必须考虑进去即一般打断会有缓存消息处理,打断的时候不要去搜网
    缓存消息一个不好的地方就是:在处理缓存的时候,会进入两遍预处理,所以,要么避免有预处理,要么在预处理的时候判断一下是否处于打断状态中(当然也可以其他方式进行),总之,尽量避免无谓地运行两遍预处理
    的逻辑。(有时候运行两遍甚至可能会出现问题),另外,缓存消息最好不好出现存两条处理,因为根本不知道第一条运行后,状态会被转移到什么地方)

  • 4.3关于流程中的打断:一般打断是用来退出某个正在进行的流程,本质上是合理的,流程有入口应该也要有出口。打断原理就是置一个标记,等消息过来后,判标记,进行流程退出(状态机的退出)
    问题在于在打断后,等消息来的这段时间里面,可能会有新的其他原本流程中也需要打断的消息过来,这个时候,就会发生冲突,一种方案就是每次判断打断标记,如果有打断标记,将后来的需要打断的标记丢弃(不缓存
    了,做一些异常回复)还有一种就是一样缓存下去处理,但是这样可能会处理不到对应的消息(因为状态变了,当然不是所有的打断消息都会有冲突的可能性,在有冲突可能的消息里面要做一下以上的这些保护处理,检查
    下万一出现这种场景的时候会不会出现问题(还有一种就是按照优先级划分,某个消息来了之后,清楚之前优先级比较低的消息后,再把本条消息进行缓存,当然,前提还是有明确的优先级概念)

  • 4.4状态机的三明治结构:预处理:通常做一些异常排除,对于可能打断的消息做好两遍预处理的逻辑检查,其余会进入状态机处理。状态机处理层:直接处理打断,或者普通的消息。处理完毕后,如果发生退栈(意思是某一个流程结束了:可能是打断,可能是成功,或者失败)处理一遍缓存消息。所以可以看到3块区域会进行处理,使用的时候,要保证外部消息(因为外部消息不可控)的独立性,方法就是尽可能放到最后一层处理, 即:要打断,处理缓存的,尽量把所有的处理都放到缓存中处理,不要在预处理,或者打断过程中处理,否则在退出状态机处理结果消息的时候就可能收到影响,而且退出原因可能千奇百怪,很难控制。不缓存的,尽量放到状态机中处理,不入状态机的情况,尽量放到预处理。 对于不进入状态机处理的预处理,可以操作全局变量,因为即使进入状态机也不产生打断。如果有打断的,在处理打断消息的时候置全局变量,因为大短,消息可能会被清空的预处理最好是只读的处理,不要去写一些全局变量,除非预处理是不进入状态机的处理,才可以慎重地分析过后,写一些全局变量:总之,在预处理中写全局变量是一个非常危险的事情。主要原因是:内部可能有多个状态机(多个流程),如果在main以外的流程中读取,或者写入这个全局变量,在预处理中就会变得非常不好控制。耦合性会非常高。 所以,另一个原则就是:全局变量的赋值和使用尽
    量放在一个状态机中进行。这样至少流程可以封闭一些。

  • 4.5由于状态机的三明治结构,会导致不同的处理流程中(本质上就是不同的if中,会有重复的判断),其实,3层状态机处理的本质就是if的逻辑结构:一条消息过来后,预处理先switch一下消息名称,然后主处理,又会再次switch一下消息名称,最后后处理,再会一次,就是状态机本身就会有很多冗余的判断,加上由于处理阶段划分的很清晰(可读性好),导致每个阶段的处理函数中可能不可避免的又会有一些重复的判断出现(状态机的这种框架,就是用一些性能来换取一些可读性,来适应更多的人合作编程)。
    2.可以先列出所有状态,然后列出所有可能收到的来自四面八方的消息,然后在excel中,或者自己写程序,用组合的方式两两组合起来,每一种组合就是一种可能性,需要写出对应的处理如果是一些不太可能出现的一场情况,可以统一在文字中进行说明,没必要一一写出了
    例如:sys_cfg状态机的状态处理:左侧第一个是状态,下面是可能收到的消息,rej表示返回错误,|表示不做任何处理

  • 5.一种迷你状态机: (不用Bsearch,而是用表+对应函数的形式),设置2级的表。 主要包含的机制:
    ①控制发送出去的消息请求数量:设置opid分配和释放函数。发送消息的时候,检查全局变量,有没有超过限定,没有的话,允许分配并发送,否则不发送。
    收到回复,根据消息中的Opid进行释放。腾出更多空间给其他消息发送。这样可以控制同时处理的消息数量。
    ②事件分发机制:由于设定2级表,第一级,选择使用哪个动作表,(即状态机选择),第二级选择处理哪个事件。由于存储的上下文是单个的实体,非数组,所以同时只能处理一种同类消息,如果来多条同类消息,则无
    法正常处理。
    ③消息解析+预处理:将消息结构体中的数据,解析成需要的形式。(在状态机处理前进行),对于解析出来的内容也是做一下检查进行一些基础的排除:收到消息的时候预先进入的函数
    相比正常状态机,少了缓存处理,后处理,内部消息处理,其他几乎没有区别。
    没有缓存机制,只存储一套实体,所以不能处理多个消息异常发送的场景(当然,可以进行丢弃,但是处理精度就下降了)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值