第一章:状态机基础回顾
大家好,我是你们的老朋友。今天咱们聊聊状态机的基础知识。别觉得基础就简单,我见过太多项目翻车,都是因为基础没打牢。
有限状态机,简称FSM。说白了,就是一个系统在有限个状态之间跳来跳去。每个状态代表一种特定的行为模式。你想想看,红绿灯就是最典型的状态机——红灯、绿灯、黄灯,三个状态循环切换。
1.1 有限状态机的核心三要素
我个人习惯把FSM拆成三块来看:
- 状态集合:系统所有可能的状态。比如一个按键消抖程序,可能有「空闲态」、「抖动态」、「稳定态」。
- 输入事件:触发状态变化的条件。比如按键按下、定时器超时。
- 转移规则:当前状态+输入事件 → 下一个状态。
核心公式:FSM = (状态集, 输入集, 转移函数, 初始状态)
我在项目中遇到过一位同事,把状态机画了20多个状态,结果代码根本没法维护。嗯,这里要注意:状态不是越多越好,够用就行。
1.2 Moore与Mealy状态机的区别
这是面试常考题,也是实际开发中容易混淆的地方。我直接说结论:
| 对比项 | Moore状态机 | Mealy状态机 |
|---|---|---|
| 输出依赖 | 只依赖当前状态 | 依赖当前状态+输入 |
| 输出时机 | 状态稳定后输出 | 输入变化时立即输出 |
| 状态数量 | 通常较多 | 通常较少 |
| 响应速度 | 慢一拍(一个时钟周期) | 快(组合逻辑直接输出) |
| 典型应用 | 控制逻辑、协议解析 | 高速数据通路、编码器 |
举个例子你就明白了。假设我们要设计一个「检测到连续三个1就报警」的系统:
- Moore方式:需要4个状态(S0~S3),每个状态对应一个输出值。输入为1时状态前进,输入为0时回到S0。输出只在状态切换后更新。
- Mealy方式:只需要3个状态(S0~S2)。当处于S2且输入为1时,输出直接拉高。响应更快,但输出可能有毛刺。
我的建议:如果对时序要求严格,优先用Moore。如果追求极致的响应速度,可以考虑Mealy,但一定要处理好组合逻辑的毛刺问题。
1.3 状态转移图的绘制方法
画状态转移图,我有一套自己的「三板斧」:
- 先列状态:把系统所有可能的状态列出来,用圆圈表示。
- 再画箭头:从每个状态出发,画出所有可能的转移路径。
- 最后标条件:在箭头上标注触发转移的输入条件。
下面是我用SVG画的一个简单状态机示例——一个两状态的按键消抖程序:
这张图里,系统只有两个状态。空闲态下,检测到按键按下就跳到确认态。确认态下,如果按键保持稳定就输出1,否则超时回到空闲态。
避坑指南:我曾经在一个通信协议项目里,漏画了某个状态下的「超时转移」,结果设备在异常情况下死锁了。排查了整整两天才找到原因。所以画图时,一定要检查每个状态的所有可能出口,包括异常情况。
1.4 状态机设计的几个实用技巧
最后分享几个我多年积累的小技巧:
- 状态命名要直观:别用S0、S1这种编号,用IDLE、WAITING、PROCESSING这种有意义的名称。三个月后你自己都看不懂S2是干嘛的。
- 画图先画异常路径:很多人只画正常流程,忽略了超时、错误、复位等异常情况。这些才是项目中最容易出bug的地方。
- 状态数量控制在5~10个:超过10个状态,建议拆分成多个子状态机。我见过一个项目把状态机画了30多个状态,最后没人敢改代码。
- 用表格辅助验证:画完状态转移图后,列一个状态转移表,逐行检查每个状态+输入组合的转移是否正确。
好了,基础就聊到这儿。这些内容看着简单,但真到了项目里,能把状态机画清楚的人其实不多。下一章咱们会深入讨论状态机的性能优化,到时候我会拿几个真实案例来拆解。
课后练习:试着画一个「自动售货机」的状态转移图。投币5元、10元,商品价格15元。考虑找零逻辑。画完后对照一下,看看有没有遗漏的异常情况。
可可精品资料库