打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
分享:用10行代码实现的C语言状态机,支持超时机制 (amoBBS 阿莫电子论坛)
 本帖最后由 mdb3 于 2015-1-15 21:32 编辑
刚逛坛子,发现有前辈总结了状态机的原理和分析方法,觉得不错,分享:http://www.amobbs.com/thread-3316176-1-1.html
个人觉得,现实中的软件,特别是嵌入式软件,至少80%都可以轻松的套用状态机思想分析和实现。

代码如下,是ansi c编写的demo,大家可以在vc货mdk中编译看看。
这个简单的状态机使用c语言函数指针实现,从我的一个STM32工程中摘出来的;
可以支持状态转换、状态超时。
反正我用这种方法,实现了自动售货机售卖、收钱、找钱、上位机通信等完整功能,已经商用一段时间,还算稳定。已经把这种思想推行到其他项目中。

实现原理简介:
每一个状态就是一个void func(void) 类型的函数,每个函数只需要知道两件事:
1)自己要做什么(业务)
2)自己的下一个状态是什么(状态切换)
就像接力棒跑一样,每个人拿到棒子后全力跑完自己那一段(业务),把棒子递给下一个人(状态迁移)。
用我这种方法实现,比switch实现的方案,好在没有集中的状态转换,而是把状态转换分散到每一个状态中,最大的优点有两个:
1)无论多复杂的业务流程,状态切换的代码都只需要负责几个分支,不用通盘考虑。
2)对流程变化更友好:哪些流程变化,修改对应状态的函数即可;没有涉及到的内容完全无需考虑,无需担心引入bug。

相比较其他框架,这个方案的唯一优点就是简单,就几行代码而已嘛,随时根据自己的需求,添加特殊代码,实现特殊功能,对g_state_timeout_milliseconds的使用就是一个典型实例。
归根结底,最重要的还是对业务需求的分析,合理的状态划分,具体实现方案,只是个工具而已


核心代码是“状态机 BEGIN ”到“状态机 END”中的那10句话。

  1. #include <time.h>
  2. #include <stdio.h>

  3. void delay_ms(int ms)
  4. {
  5.     //系统延时函数
  6. }

  7. unsigned int systemMs(void)
  8. {
  9.     clock_t c = clock()+100;
  10.     return c;
  11. }

  12. /************ 状态机 BEGIN *****************/
  13. typedef void (*state_func_t)(void);
  14. static unsigned int g_state_timeout_milliseconds=0;
  15. static state_func_t g_state_func = NULL;

  16. void main_setNextState(state_func_t func)
  17. {
  18.     /*
  19.      * g_state_timeout_milliseconds
  20.      * 可以用于判断是否进入一个新状态;
  21.      * 还可以用于判断进入某个状态多长时间了。
  22.      */
  23.     g_state_timeout_milliseconds = 0;

  24.     g_state_func = func;
  25. }
  26. state_func_t main_getNextState(void)
  27. {
  28.     return g_state_func;
  29. }
  30. /************ 状态机 END *****************/

  31. /************ 状态函数 BEGIN *************/
  32. void main_state1(void);
  33. void main_state2(void);

  34. void main_state1(void)
  35. {
  36.     if(0 == g_state_timeout_milliseconds) {
  37.         g_state_timeout_milliseconds = systemMs();
  38.         printf("enter state1\n");
  39.     }
  40.     /* 5000毫秒超时 */
  41.     if(systemMs() - g_state_timeout_milliseconds > 5*1000) {
  42.         printf("exit state1\n");
  43.         main_setNextState(main_state2);
  44.         return;
  45.     }

  46.     /*
  47.      * TODO 这里实现业务逻辑
  48.      * 比如到达某种条件,进入其他状态
  49.      * 比如收到某条消息,进入其他状态
  50.      */
  51. }
  52. void main_state2(void)
  53. {
  54.     if(0 == g_state_timeout_milliseconds) {
  55.         g_state_timeout_milliseconds = systemMs();
  56.         printf("enter state2\n");
  57.     }
  58.     /* 2000毫秒超时 */
  59.     if(systemMs() - g_state_timeout_milliseconds > 2*1000) {
  60.         printf("exit state2\n");
  61.         main_setNextState(main_state1);
  62.         return;
  63.     }

  64.     /*
  65.      * TODO 这里实现业务逻辑
  66.      * 比如到达某种条件,进入其他状态
  67.      * 比如收到某条消息,进入其他状态
  68.      */
  69. }
  70. /************ 状态函数 END *************/

  71. int main(int argc, char *argv[])
  72. {
  73.     printf("Hello, state machine!\n");
  74.     /*
  75.      * 设置初始状态
  76.      */
  77.     main_setNextState(main_state1);

  78.     /*
  79.      * 状态驱动
  80.      * 每一个状态,处理完自己的事情后,负责指定下一个状态(使用main_setNextState函数)
  81.      */
  82.     while(1) {
  83.         (*main_getNextState())();
  84.         delay_ms(1);
  85.     }
  86.     return 0;
  87. }
复制代码


==================1月15日添加==================
看了下面的评论,看来很多同学不理解状态机,比如拿状态机和switch比较,甚至拿状态机和RTOS比较、协作库比较。
状态机最精妙的应用,同时也是最一般的应用,就是对一个复杂的业务流程解耦合,实现“高内聚、低耦合”的至高境界。
状态机是23个设计模式中的一个,实现方案、框架也非常多,我这里的这个仅仅是根据自己的需要实现的的一个最简模型,完全手写。
systemMs()函数的作用,就是返回系统从启动到现在,经过了多少个毫秒
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
c++中的哑元函数的使用 与c函数声明那块区别开来
[原]JavaScript的window.setTimeout()方法
GCC警告选项例解
Linux进程-进程的退出
C/C 语言中闭包的探究及比较 | 酷壳
“undefined reference to” 问题汇总及解决方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服