Skip to the content.

转载来源:阿莫论坛-傻孩子专栏

什么地方用状态机?
状态机究竟有几种写法?
状态机效率到底高不高?
是不是把简单问题弄复杂了?
这类问题统统不在本文讨论之列,简而言之——谁用谁知道。
其实,还不能简单的就这么下了结论,套八股文而不求甚解的也大有人在。
————因此我要说:关于状态机的各种问题“谁思考谁实践谁坚持谁知道”。

状态机入门第一式:switch case一线到底

要点: 用switch结构配合一个状态变量,通过修改状态变量的值来切换状态。 范例:

//! 定义状态名称与状态值之间的关系,增加可读性
#define FSM_START                                   0x00
#define FSM_STATE_A                                 0x01
#define FSM_STATE_B                                 0x02

#define FSM_RESET                                   0xFF
bool fsm_example_A( <形参列表> ) {
    static uint8_t s_chFSMState = FSM_START;                //!< 定义状态变量
    
    switch ( s_chFSMState ) {
    case FSM_START:
    //! 这里添加状态机初始化代码
    
    s_chFSMState = FSM_STATE_A;                             //!< 进入下一状态
    break;
    case FSM_STATE_A:
    //! 这里添加状态机A进入下一状态的检测代码
    if (<某某条件>) {
        //! 这里做一些进入下一状态时要做的准备工作
        s_chFSMState = FSM_STATE_B;                         //!< 进入下一状态
    }
    break;
    case FSM_STATE_B:
    //! 这里添加状态机A进入下一状态的检测代码
    if (<某某条件>) {
        //! 这里做一些进入下一状态时要做的准备工作
        s_chFSMState = FSM_STATE_A;                         //!< 进入下一状态
    } else if (<某某条件>) {
    } else if (<某某条件>) {
    
    } else {
    }
    break;
    
    case FSM_STOP:
    case FSM_RESET:
    default:
    //! 这里添加状态机复位相关的代码
    
    chFSMState = FSM_START;                                 //!< 状态机复位

    //! 返回false表示状态机已经不需要继续运行了
    return false;
    }

    //! 返回true表示状态机正在运行
    return true;`
}

总结: 从范例可知,这种状态机就是一根筋……并不是说他走不出什么分支来,而 是说通常他没有办法让多个状态同时处于激活状态。

状态机入门第二式:if 判断变化无穷

要点: 用if else…else if结构的组合来描述状态流程图。 什么是状态流程图?我不想多解释,因为就那么个简单的东西,说多了反而神秘兮兮的。 状态流程图你可以简单粗暴的认为,他就是流程图,等你用得多了,你就渐渐明白为啥 多了“状态”二字;如果你后来或者先前学过状态图,那么很快你就会明白状态流程图 比状态图“高级”了多少。 1、 不管怎么说,你可以先为你要处理的事物画一个流程图。如果流程图都不会画,就 不用凑热闹了。 2、 接下来,把流程图上每一个方框或者判断筐都“简单粗暴”地看成一个状态。 3、 将每一个状态用if结构表示出来

if (<状态标志>) {
    //! 状态代码
    
}

4、 自己看着办,合并多余的状态,优化优化代码。 范例:

//! 首先将布尔量的状态标志压缩在一个字节里面以节省内存开支

typedef union {
    uint8_t     Value;
    uint8_t     Byte;
    struct {
        unsigned BIT0:1;
        unsigned BIT1:1;
        unsigned BIT2:1;
        unsigned BIT3:1;
        unsigned BIT4:1;
        unsigned BIT5:1;
        unsigned BIT6:1;
        unsigned BIT7:1;
    }           Bits;
}byte_t;

#define FSM_ACTION_FLAG             s_tbState.Bits

#define FSM_STOP_ALL_ACTIONS()      do {s_tbState.Value = 0;}while(0)

#define FSM_START                   (0 == s_tbState.Value)

#define FSM_STATE_A                 FSM_ACTION_FLAG.BIT0

#define FSM_STATE_B                 FSM_ACTION_FLAG.BIT1



#define FSM_STATE_H                 FSM_ACTION_FLAG.BIT7

bool fsm_example_B( <形参列表> ) {
    static byte_t s_tbState = {0};                              //!< 定义状态变量

    if (FSM_START) {                                            //!< 起始状态
        //! 这里放置状态机初始化的代码
        
        FSM_STATE_A = true;                                     //!< 进入状态B,start装台自动结束
    }

    if (FSM_STATE_A) {                                          //!< 一个典型的简单状态
        //! 这里放置状态A的代码或者
        
        //! 这里放置某些条件以开启别的状态
        if (<某些条件>) {
            //! 这里做一些“进入”下一个状态之前的准备工作
            FSM_STATE_B = true;                                 //!< 开启下一个状态
            FSM_STATE_A = false;                                //!< 结束当前状态
        }
    }

    if (FSM_STATE_B) {                                          //!< 一个典型的监视状态
        
        //! 这里检测某些条件
        if (<某些条件>) {
            //! 这里做一些“开启”某个状态的准备工作
            FSM_STATE_C = true;                                 //!< 开启某一个状态而不结束当前状态
            FSM_STATE_D = true;                                 //!< 你当然可以一次触发多个状态
            
        } else if (<某些条件>) {
            //! 满足某些条件以后关闭当前状态
            FSM_STATE_B = false;
        }
    }
    
    if (FSM_STATE_F) {                                          //!< 一个典型的子状态机调用
        if (!fsm_example_a(<实参列表>)) {                       //!< 等待子状态机返回false
            //!子状态机运行完成,进入下一状态
            
            FSM_STATE_F = false;                                //!< 结束当前状态
            FSM_STATE_x = true;                                 //!< 进入下一状态x代表某个字母
        }
    }

    if (FSM_STATE_H) {                                          //!< 一个典型的中止状态
        //!< 某些状态机的操作,比如释放某些资源
        
        FSM_STOP_ALL_ACTIONS();                                 //!< 复位状态机
        return false;                                           //!< 返回false表示状态机结束
    }
    return true;                                                //!< 返回true表示状态机保持运行
}

总结: 从范例可知,这种状态机非常灵活,通过布尔变量的开启和关闭,你可以自 由的控制某些状态的开启。同一时刻可能有多个状态是激活的。这种结构几乎可以翻译 任何流程图。具体还有很多好处,可以在使用中体会。

状态机入门第三式:状态在心中,无态也变态

要点:

所有的函数都可以看作是状态机,只不过普通的函数是一个只有单一状态的状态机。
如果函数有返回值,且这个返回值能表征至少两种以上不同的状态:
比如返回是一个指针,那么NULL和非NULL就是两种状态;
比如返回是一个布尔变量,那么true和false就是两种状态;
比如返回的是一个整数,并且整数的某些特征可以被分类,那么这些不同分类就是几种不同的状态),
那么这些返回值就可以被用作指示当前状态机的运行情况。
状态机可以调用子状态机。所有的状态都应该是none-block的,
简单说就是不会把系统定死在某一个状态里面很久都出不来,
比如while(1)或者循环次数较大的for结构;否则状态机的存在意义就大打折扣。
状态机中,状态的功能应该是等待某一个事件的发生(或者说条件的满足);
某些情况下,一些一次性执行完的流程也可以独立成一个状态,
它当然没有等待任何条件的满足,你可以认为他是无条件进行状态转移的。

总体说来,状态机是一个万能的计算机语言表述方式,与具体的载体语言关系不大。
心中有状态,代码怎会无状态?状态机是裸机条件下多任务的廉价实现方案。
在状态机多任务条件下,操作系统牵涉的几乎所有概念都会有所涉及,
比如任务的同步,临界区的保护,任务间的通讯,任务的优先级,资源的动态分配等等。
你可以这么理解,每一个状态机都是一个进程,每一个状态都是一个线程,
因为进程有自己的资源,而同一个进程里面的多个线程是公用同一片资源的。

博客主页