Linux 编程之有限状态机 FSM 的理解与实现
有限状态机 (Finite State Machine,简称FSM) 是指由有限个状态以及在这些状态之间的转移和动作等行为组成的数学模型,在计算机领域得到了广泛的应用。FSM 是一种高效的编程方法,用于在逻辑单元内部实现程序的处理逻辑,特别是在服务器编程中,通过基于不同的状态或消息类型进行相应的处理,可以使程序的逻辑更加清晰易懂。
那么,有限状态机通常在哪些地方被使用呢?
它可以应用于处理程序语言或自然语言的分词器 (tokenizer),通过自底向上的语法解析器 (parser) 实现语法的解析和分析,在各种通信协议中,发送方和接收方之间通过传递数据来处理消息,在游戏人工智能中等等。
对于有限状态机的实现,一般有以下几种方法,我将逐一介绍它们的优缺点。
一、使用 if/else if 语句实现的有限状态机
使用 if/else if 语句实现有限状态机是最简单、易于理解的方法。只需要使用大量的 if/else if 语句来判断当前的状态,并执行相应的逻辑处理。
下面是一个简单的状态机示例,我们使用了大量的 if/else if 语句来实现,根据不同的状态执行相应的操作,并实现状态的转换。
enum { GET_UP, GO_TO_SCHOOL, HAVE_LUNCH, GO_HOME, DO_HOMEWORK, SLEEP, }; int main() { int state = GET_UP; //小明的一天 while ( 1 ) { if (state == GET_UP) { GetUp (); //具体调用的函数 state = GO_TO_SCHOOL; //状态的转移 } else if (state == GO_TO_SCHOOL) { Go2School (); state = HAVE_LUNCH; } else if (state == HAVE_LUNCH) { HaveLunch (); } ... else if (state == SLEEP) { Go2Bed (); state = GET_UP; } } return 0 ; }
看完上面的例子,大家有什么感受?是不是感觉程序虽然简单易懂,但是使用了大量的 if 判断语句,使得代码很低端,同时代码膨胀的比较厉害。这个状态机的状态仅有几个,代码膨胀并不明显,但是如果我们需要处理的状态有数十个的话,该状态机的代码就不好读了。
二、使用 switch 实现 FSM
使用 switch 语句实现的 FSM 的结构变得更为清晰了,其缺点也是明显的:这种设计方法虽然简单,通过一大堆判断来处理,适合小规模的状态切换流程,但如果规模扩大难以扩展和维护。
int state = GET_UP; //小明的一天 while ( 1 ) { switch (state) { case GET_UP: GetUp (); //具体调用的函数 state = GO_TO_SCHOOL; //状态的转移 break ; case GO_TO_SCHOOL: Go2School (); state = HAVE_LUNCH; break ; case HAVE_LUNCH: HaveLunch (); state = GO_HOME; break ; ... default : break ; } } return 0 ; }
三、使用函数指针实现 FSM
使用函数指针实现 FSM 的思路:建立相应的状态表和动作查询表,根据状态表、事件、动作表定位相应的动作处理函数,执行完成后再进行状态的切换。
当然使用函数指针实现的 FSM 的过程还是比较费时费力,但是这一切都是值得的,因为当你的程序规模大时候,基于这种表结构的状态机,维护程序起来也是得心应手。
下面给出一个使用函数指针实现的 FSM 的框架:
我们还是以 “小明的一天” 为例设计出该 FSM。
先给出该 FSM 的状态转移图:

下面讲解关键部分代码实现
首先我们定义出小明一天的活动状态:
//比如我们定义了小明一天的状态如下
enum { GET_UP, GO_TO_SCHOOL, HAVE_LUNCH, DO_HOMEWORK, SLEEP, };
我们也定义出会发生的事件
{ EVENT1 = 1 , EVENT2, EVENT3, };
定义状态表的数据结构
typedef struct FsmTable_s { int event ; //事件 int CurState ; //当前状态 void (*eventActFun)(); //函数指针 int NextState ; //下一个状态 } FsmTable_t ;
接下来定义出最重要 FSM 的状态表,我们整个 FSM 就是根据这个定义好的表来运转的。
FsmTable_t XiaoMingTable [] = { //{到来的事件,当前的状态,将要要执行的函数,下一个状态} { EVENT1, SLEEP, GetUp , GET_UP }, { EVENT2, GET_UP, Go2School , GO_TO_SCHOOL }, { EVENT3, GO_TO_SCHOOL, HaveLunch , HAVE_LUNCH }, { EVENT1, HAVE_LUNCH, DoHomework , DO_HOMEWORK }, { EVENT2, DO_HOMEWORK, Go2Bed , SLEEP }, //add your codes here };
状态机的注册、状态转移、事件处理的动作实现
/*状态机注册*/ void FSM_Regist( FSM_t * pFsm, FsmTable_t * pTable) { pFsm-> FsmTable = pTable; } /*状态迁移*/ void FSM_StateTransfer( FSM_t * pFsm, int state) { pFsm->curState = state; } /*事件处理*/ void FSM_EventHandle( FSM_t * pFsm, int event ) { FsmTable_t * pActTable = pFsm-> FsmTable ; void (*eventActFun)() = NULL; //函数指针初始化为空 int NextState ; int CurState = pFsm->curState; int flag = 0 ; //标识是否满足条件 int i; /*获取当前动作函数*/ for (i = 0 ; iif ( event == pActTable[i]. event && CurState == pActTable[i]. CurState ) { flag = 1 ; eventActFun = pActTable[i].eventActFun; NextState = pActTable[i]. NextState ; break ; } } if (flag) //如果满足条件了 { /*动作执行*/ if (eventActFun) { eventActFun(); } //跳转到下一个状态 FSM_StateTransfer(pFsm, NextState ); } else { // do nothing } }
主函数我们这样写,然后观察状态机的运转情况。
int main() { FSM_t fsm; InitFsm (&fsm); int event = EVENT1; //小明的一天,周而复始的一天又一天,进行着相同的活动 while ( 1 ) { printf( "event %d is coming...\n" , event ); FSM_EventHandle(&fsm, event ); printf( "fsm current state %d\n" , fsm.curState); test(& event ); sleep( 1 ); //休眠1秒,方便观察 } return 0 ; }
看一看该状态机跑起来的状态转移情况:

上面的图可以看出,当且仅当在指定的状态下来了指定的事件才会发生函数的执行以及状态的转移,否则不会发生状态的跳转。这种机制使得这个状态机不停地自动运转,有条不絮地完成任务。
与前两种方法相比,使用函数指针实现 FSM 能很好用于大规模的切换流程,只要我们实现搭好了 FSM 框架,以后进行扩展就很简单了(只要在状态表里加一行来写入新的状态处理就可以了)。
以上是Linux 编程之有限状态机 FSM 的理解与实现的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Linux系统的五个基本组件是:1.内核,2.系统库,3.系统实用程序,4.图形用户界面,5.应用程序。内核管理硬件资源,系统库提供预编译函数,系统实用程序用于系统管理,GUI提供可视化交互,应用程序利用这些组件实现功能。

要查看 Git 仓库地址,请执行以下步骤:1. 打开命令行并导航到仓库目录;2. 运行 "git remote -v" 命令;3. 查看输出中的仓库名称及其相应的地址。

VS Code 一步/下一步快捷键的使用方法:一步(向后):Windows/Linux:Ctrl ←;macOS:Cmd ←下一步(向前):Windows/Linux:Ctrl →;macOS:Cmd →

Linux的主要用途包括:1.服务器操作系统,2.嵌入式系统,3.桌面操作系统,4.开发和测试环境。Linux在这些领域表现出色,提供了稳定性、安全性和高效的开发工具。

虽然 Notepad 无法直接运行 Java 代码,但可以通过借助其他工具实现:使用命令行编译器 (javac) 编译代码,生成字节码文件 (filename.class)。使用 Java 解释器 (java) 解释字节码,执行代码并输出结果。

在 Sublime 中运行代码的方法有六种:通过热键、菜单、构建系统、命令行、设置默认构建系统和自定义构建命令,并可通过右键单击项目/文件运行单个文件/项目,构建系统可用性取决于 Sublime Text 的安装情况。

要安装 Laravel,需依序进行以下步骤:安装 Composer(适用于 macOS/Linux 和 Windows)安装 Laravel 安装器创建新项目启动服务访问应用程序(网址:http://127.0.0.1:8000)设置数据库连接(如果需要)

Visual Studio Code (VSCode) 是一款跨平台、开源且免费的代码编辑器,由微软开发。它以轻量、可扩展性和对众多编程语言的支持而著称。要安装 VSCode,请访问官方网站下载并运行安装程序。使用 VSCode 时,可以创建新项目、编辑代码、调试代码、导航项目、扩展 VSCode 和管理设置。VSCode 适用于 Windows、macOS 和 Linux,支持多种编程语言,并通过 Marketplace 提供各种扩展。它的优势包括轻量、可扩展性、广泛的语言支持、丰富的功能和版
