有限状态机FSM

关于状态机,以前写过用Go实现一个状态机,只是讲述了如何控制状态的流转,理论上不能算作完整的状态机。

一个完整的状态机其过程如下:发生一个event(事件)后,根据当前存在的状态(cur-state),决定执行的“动作”(action),并设置下一个状态号(transition)。

其中:

  1. 事件(Event)指的是在时间和空间上占有一定位置,并且对状态机来讲是有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。

  2. 状态(State)指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件。

  3. 转换(Transition)指的是两个状态之间的一种关系,表明对象将在第一个状态中执行一定的动作,并将在某个事件发生同时某个特定条件满足时进入第二个状态。

这次我们看一下有限状态机及其实战。

FSM

定义

有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

FSM可以把模型的多状态、多状态间的转换条件解耦。可以使维护变得容易,代码也更加具有可读性,也更加艺术。

源码

样例

github上https://github.com/looplab/fsm,有1.8K Star,我们以这个开源项目为例,讲一下FSM的具体实现。此处用开门、关门为例,状态虽然很,但能很好的说明问题。

样例代码位置为:https://github.com/shidawuhen/asap/blob/master/controller/various/fsm.go

package main

import (
	"fmt"
	"github.com/looplab/fsm"
)

type Door struct {
	To  string
	FSM *fsm.FSM
}

func NewDoor(to string) *Door {
	d := &Door{
		To: to,
	}

	d.FSM = fsm.NewFSM(
		"closed",
		fsm.Events{
			{Name: "open", Src: []string{"closed"}, Dst: "open"},
			{Name: "close", Src: []string{"open"}, Dst: "closed"},
		},
		fsm.Callbacks{
			//指定状态
			"leave_closed": func(e *fsm.Event) { d.leaveClose(e) },
			"before_open":  func(e *fsm.Event) { d.beforeOpen(e) },
			"enter_open":   func(e *fsm.Event) { d.enterOpen(e) },
			"after_open":   func(e *fsm.Event) { d.afterOpen(e) },
			//通用状态
			"enter_state": func(e *fsm.Event) { d.enterState(e) },
		},
	)
	return d
}

func (d *Door) beforeOpen(e *fsm.Event) {
	fmt.Printf("beforeOpen, The door to %s is %s\n", d.To, e.Dst)
}

func (d *Door) enterOpen(e *fsm.Event) {
	fmt.Printf("enterOpen, The door to %s is %s\n", d.To, e.Dst)
}

func (d *Door) afterOpen(e *fsm.Event) {
	fmt.Printf("afterOpen, The door to %s is %s\n", d.To, e.Dst)
}

func (d *Door) leaveOpen(e *fsm.Event) {
	fmt.Printf("leaveOpen, The door to %s is %s\n", d.To, e.Dst)
}

func (d *Door) leaveClose(e *fsm.Event) {
	fmt.Printf("leaveClose, The door to %s is %s\n", d.To, e.Dst)
}

func (d *Door) enterState(e *fsm.Event) {
	fmt.Printf("The door to %s is %s\n", d.To, e.Dst)
}

func main() {
	door := NewDoor("heaven")
	fmt.Println("当前状态为:" + door.FSM.Current())

	err := door.FSM.Event("open")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("当前状态为:" + door.FSM.Current())
	err = door.FSM.Event("close")
	if err != nil {
		fmt.Println(err)
	}
}

输出:

➜  myproject go run main.go
当前状态为:closed
beforeOpen, The door to heaven is open
leaveClose, The door to heaven is open
enterOpen, The door to heaven is open
The door to heaven is open
afterOpen, The door to heaven is open
当前状态为:open
The door to heaven is closed

说明
  1. NewDoor里的Events存放状态机的信息
  • Name:事件

  • Src:当前状态

  • Dst:目标状态

表示某事件发生时,如果当前为Src状态,可变换为Dst状态

  1. NewDoor里的Callbacks存放转换动作
  • 转换动作可分为通用转换和指定转换,通用转换状态格式为***_state,指定转换状态格式为***_状态名

  • 无论通用还是指定转换状态,都是四种,分别对应样例中的代码

  • 进入当前状态前做什么:before_**

  • 离开上一个状态做什么:leave_**

  • 进入当前状态做什么:enter_**

  • 当前状态执行完做什么:after_**

  • 执行顺序为:https://www.processon.com/view/link/6289e3bd1e08533ae716e7ad

图片

实例


FSM可以用在状态多、变化也多的地方,如履约单。一般订单履约涉及很多状态,而且这些状态经常会变更,使用FSM会方便很多。其中有几个实现要点:

  1. 订单上要记录当前状态

  2. 需要维护状态机,可以从两方面考虑

  • 画图:清晰的资料能帮我们快速了解当前整个状态机的情况

  • 状态机存储:可以将状态机写在代码中或存放到数据库中,格式如Events所示,至少需要有事件、当前状态、目标状态

  1. 转移实现
  • 转移有通用转移和指定转移,好的通用转移逻辑能增强复用性

  • 指定状态可以独立实现,因为转换代码已解耦,对系统影响很小

  1. 状态一致性
  • 先计算出目标状态,当正确完成所有操作后,更新订单状态为目标状态

总结

通过分析源码和实例,大家能够看到使用FSM不但能清晰维护状态机,而且对状态的更改、对转移功能的更改都实现了解耦,大大减少了维护成本。

同时通过合理的设计,赋予研发人员对状态转移操作极大的控制性,可以从离开、进入前、进入、完成后四个时机进行控制。

在友好程度上,我觉得是比Go设计模式(22)-状态模式更好一些的。

资料

  1. 有限状态机FSM详解(一)

  2. FSM学习笔记

  3. 状态机的两种写法

  4. Gofsm

  5. https://github.com/looplab/fsm

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

往期文章回顾:

  1. 设计模式

  2. 招聘

  3. 思考

  4. 存储

  5. 算法系列

  6. 读书笔记

  7. 小工具

  8. 架构

  9. 网络

  10. Go语言

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
利用 VHDL 设计的许多实用逻辑系统中 有许多是可以利用有限状态机的设计方案来 描述和实现的 无论与基于 VHDL 的其它设计方案相比 还是与可完成相似功能的 CPU 相比 状态机都有其难以逾越的优越性 它主要表现在以下几方面 h 由于状态机的结构模式相对简单 设计方案相对固定 特别是可以定义符号化枚 举类型的状态 这一切都为 VHDL 综合器尽可能发挥其强大的优化功能提供了有利条件 而且 性能良好的综合器都具备许多可控或不可控的专门用于优化状态机的功能 h 状态机容易构成性能良好的同步时序逻辑模块 这对于对付大规模逻辑电路设计 中令人深感棘手的竞争冒险现象无疑是一个上佳的选择 加之综合器对状态机的特有的优 化功能 使的状态机解决方案的优越性更为突出 h 状态机的 VHDL 设计程序层次分明 结构清晰 易读易懂 在排错 修改和模块 移植方面 初学者特别容易掌握 h 在高速运算和控制方面 状态机更有其巨大的优势 由于在 VHDL 中 一个状态 机可以由多个进程构成 一个结构体中可以包含多个状态机 而一个单独的状态机 或多 个并行运行的状态机 以顺序方式的所能完成的运算和控制方面的工作与一个 CPU 类似 由此不难理解 一个设计实体的功能便类似于一个含有并行运行的多 CPU 的高性能微处 理器的功能 事实上这种多 CPU 的微处理器早已在通信 工控和军事等领域有了十分广 泛的应用 h 就运行速度而言 尽管 CPU 和状态机都是按照时钟节拍以顺序时序方式工作的 但 CPU 是按照指令周期 以逐条执行指令的方式运行的 每执行一条指令 通常只能完 成一项操作 而一个指令周期须由多个 CPU 机器周期构成 一个机器周期又由多个时钟 周期构成 一个含有运算和控制的完整设计程序往往需要成百上千条指令 相比之下 状 态机状态变换周期只有一个时钟周期 而且 由于在每一状态中 状态机可以完成许多并 行的运算和控制操作 所以 一个完整的控制程序 即使由多个并行的状态机构成 其状 态数也是十分有限的 因此有理由认为 由状态机构成的硬件系统比 CPU 所能完成同样 功能的软件系统的工作速度要高出两个数量级 h 就可靠性而言 状态机的优势也是十分明显的 CPU 本身的结构特点与执行软件 指令的工作方式决定了任何 CPU 都不可能获得圆满的容错保障 这已是不争的事实了 因此 用于要求高可靠性的特殊环境中的电子系统中 如果以 CPU 作为主控部件 应是 一项错误的决策 然而 状态机系统就不同了 首先是由于状态机的设计中能使用各种无 懈可击的容错技术 其次是当状态机进入非法状态并从中跳出所耗的时间十分短暂 通常 只有 2 个时钟周期 约数十个 ns 尚不足以对系统的运行构成损害 而 CPU 通过复位方第 10 章 有限状态机 FSM 199 式从非法运行方式中恢复过来 耗时达数十 ms 这对于高速高可靠系统显然是无法容忍 的 再其次是状态机本身是以并行运行为主的纯硬件结构

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值