有限状态机的工作原理:发生事件(event)后,根据当前状态(cur_state) ,决定执行的动作(action),并设置下一个状态号(nxt_state)。
状态机可以用两种方法实现:竖着写(在状态中判断事件)和横着写(在事件中判断状态)。这两种实现在本质上是完全等效的,但在实际操作中,效果却截然不同。
- 竖着写(在状态中判断事件)C代码片段:
cur_state = nxt_state;
- switch(cur_state) //在当前状态中判断事件
- {
- case s0: //在s0状态
- if(e0_event) //如果发生e0事件,那么就执行a0动作,并保持状态不变;
- {
- //执行a0动作;
- //nxt_state = s0; //因为状态号是自身,所以可以删除此句,以提高运行速度。
- }
- else if(e1_event) //如果发生e1事件,那么就执行a1动作,并将状态转移到s1态;
- {
- //执行a1动作;
- nxt_state = s1;
- }
- else if(e2_event) //如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
- {
- //执行a2动作;
- nxt_state = s2;
- }
- else
- {
- break;
- }
- case s1: //在s1状态
- if(e2_event) //如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
- {
- //执行a2动作;
- nxt_state = s2;
- }
- else
- {
- break;
- }
- case s2: //在s2状态
- if(e0_event) //如果发生e0事件,那么就执行a0动作,并将状态转移到s0态;
- {
- //执行a0动作;
- nxt_state = s0;
- }
- }
- 横着写(在事件中判断状态)C代码片段:
- //e0事件发生时,执行的函数
- void e0_event_function(int * nxt_state)
- {
- int cur_state;
- cur_state = *nxt_state;
- switch(cur_state)
- {
- case s0: //观察表1,在e0事件发生时,s1处为空
- case s2: //执行a0动作;
- *nxt_state = s0;
- }
- }
- //e1事件发生时,执行的函数
- void e1_event_function(int * nxt_state)
- {
- int cur_state;
- cur_state = *nxt_state;
- switch(cur_state)
- {
- case s0: //观察表1,在e1事件发生时,s1和s2处为空
- //执行a1动作;
- *nxt_state = s1;
- }
- }
- //e2事件发生时,执行的函数
- void e2_event_function(int * nxt_state)
- {
- int cur_state;
- cur_state = *nxt_state;
- switch(cur_state)
- {
- case s0: //观察表1,在e2事件发生时,s2处为空
- case s1:
- //执行a2动作;
- *nxt_state = s2;
- }
- }
上面横竖两种写法的代码片段,实现的功能完全相同,但是,横着写的效果明显好于竖着写的效果。理由如下:
1、竖着写隐含了优先级排序(其实各个事件是同优先级的),排在前面的事件判断将毫无疑问地优先于排在后面的事件判断。这种if/else if写法上的限制将破坏事件间原有的关系。而横着写不存在此问题。
2、由于处在每个状态时的事件数目不一致,而且事件发生的时间是随机的,无法预先确定,导致竖着写沦落为顺序查询方式,结构上的缺陷使得大量时间被浪费。对于横着写,在某个时间点,状态是唯一确定的,在事件里查找状态只要使用switch语句,能一步定位到相应的状态,延迟时间可以预先准确估算。而且在事件发生时,调用事件函数,在函数里查找唯一确定的状态,并根据其执行动作和状态转移的思路清晰简洁,效率高,富有美感。
竖着写的方法也不是完全不能使用,在一些小项目里,逻辑不太复杂,功能精简,同时为了节约内存耗费,竖着写的方法也不失为一种合适的选择。