前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >相亲模型与有限状态机

相亲模型与有限状态机

作者头像
CS实验室
发布于 2022-08-01 12:16:51
发布于 2022-08-01 12:16:51
49600
代码可运行
举报
文章被收录于专栏:CS实验室CS实验室
运行总次数:0
代码可运行

如果有人问你需要几步可以把大象关进冰箱里,你脑海中肯定浮现起宋大妈的笑容并脱口而出:3步。

如果有人问你从单身到定终身需要几步,估计你还真需要掰着手指算一算。

相亲模型

一入编程深似海,代码尽头是相亲。有前辈说过,没有相过亲的程序猿是不完整的,连写出来的代码都少一分深沉。虽说没有亲身体验,但如果掰着手指算算定终身的事情,倒也可以从相亲说起。

从相亲说起

你清清嗓子,咽了口水,双指快速摩挲着菜单的边角,抬头望着桌对面的陌生漂亮小姐姐:“你有啥想吃的么”。

“随便吃点就好”。

你继续埋头到菜单之中,胡乱的点了几个菜,匆忙的连饮料都忘了点。

“那个,我先自我介绍一下”,你把菜单一推,心想大不了当 HR 面试了,开始介绍起自己的来,还顺便提了一句自己不写 Java

饭后回家,回想起吃饭的点滴,尴尬阵阵涌起,不过好在相谈甚欢,仿佛有戏。意犹未尽之后,你打开某绿色聊天 app,点开熟悉又陌生的头像,开始了下半场。

次月,又是这家饭馆。你决定捅破那层窗户纸,咖啡馆、剧本杀均留下来了共同的美好回忆,说不好时机已经成熟。

你清清嗓子,咽了口水,双指快速摩挲着菜单的边角,抬头望着桌对面的小姐姐:“你有啥想吃的么”。

相亲状态流转图

恐怕只有像我这样蹩脚的人才会写出如此没有起伏的故事,为何?因为情感是世间最复杂的事情之一,而以聊天为打法,以推动情感发展为底层逻辑的相亲也是变量最多的战役。虽不能穷举相亲的过程,但是尝试把相亲的场景过程抽象量化,想必可以得到一张复杂的状态流转图。

上面这张图,月老看了都直呼内行。相亲过程中的中间状态只多不少,那么从单身到定终身需要几步呢?

有限状态机

有限状态机(FSM)本是控制论的一个数学模型。用来表示可枚举种类的状态之间的转移和动作等行为。说人话就是用来控制机器状态的变更。

基本概念

从相亲的模型中可以看到,一个有限状态机包含『状态』和『行为』两大基本概念。

『状态』包括两个核心元素:

  • 第一个是 State 即状态,一个状态机至少要包含两个状态。比如最简单的状态机就是电灯,包含了 “亮” 和 “灭” 两个状态。
  • 第二个是 Transition 即状态流转,也就是从一个状态变化为另一个状态。比如 “开灯的过程” 就是一个状态流转。

『行为』也包含两个核心元素

  • 第一个是 Event 即事件,事件就是执行某个操作,也可以看做外部系统的输入。比如 “按下灯开关” 就是一个事件。
  • 第二个是 Action 即动作,事件发生后引发的变更,也可以看做状态机对外部的输出。比如 “按下灯开关” 事件后,会触发 “灯泡亮起” 这个动作。

适用场景

有限状态机的适用场景很多,尤其是状态复杂的场景,比如订单、任务管理等。有限状态机的本质是维护状态流转图,使得在复杂的用户输入中,依然保持状态的合法和安全。

(图来自《京东京麦交易平台设计与实现》)

除了复杂状态流转的场景,当状态无法明确的情况下,有限状态机也可以被考虑。仿佛和『有限』这个字眼相悖,这里的无法明确是指需求无法明确,也许今天 3 个状态就满足需求了,但下个版本可能需要 5 个状态。对于有限状态机来说,多加两种状态只不过是在状态流转图了多几条边而已。

有限状态机实现

本节的实现以 GoFlow 为例,GoFlow 是一个用 Golang 实现的任务引擎 代码可见:https://github.com/basenana/go-flow/tree/main/fsm

MIT 数位系统概论中总结了一套实现 FSM 的方法论:

  • 明确状态类型(identify distinct states)
  • 创建状态流转图(create state transition diagram)
  • 编写状态流转逻辑(write for next-state logic)
  • 编写输出信号逻辑(write for output signals)

我们就从这个四个角度讲下 GoFlow 的 FSM 实现。

枚举状态类型

首先是明确『有限』的状态,对于 GoFlow 这种任务管理系统来说,状态肯定是围绕任务展开的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const (
 CreatingStatus     fsm.Status = "creating"
 InitializingStatus            = "initializing"
 RunningStatus                 = "running"
 SucceedStatus                 = "succeed"
 FailedStatus                  = "failed"
 ErrorStatus                   = "error"
 PausedStatus                  = "paused"
 CanceledStatus                = "canceled"
)

状态流转图

状态流转图是一个有向图,每个状态都是一个点,两点之间的边即是事件,边的方向是流转方向。最简单存储方式有两种:

  1. 十字链表法:以当前状态为维度维护指向其前驱节点的边。也就是以当前状态为 Key,保存的 Value 即是事件类型和可以流转的下一级状态
  2. 边存储法:以事件为维度维护所有的边。也就是事件类型为 Key,Value 是上级状态和下级状态

两种存储方式各有好处,因为 GoFlow 状态流转图的边的数量比较少,也就选择了第二种方式:基于 Map 以事件类型为 Key,存储所有的边。这种存储方式可以快速通过一个事件,找到能执行的所有操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type edge struct {
 from Status
 to   Status
 when EventType
 do   Handler
 next *edge
}

type FSM struct {
 obj   Stateful
 graph map[EventType]*edge

 crtBuilder *edgeBuilder
 mux        sync.Mutex
 logger     log.Logger
}

从 GoFlow 的状态枚举可以看出当前的引擎相对简单,因此它的状态流转图也并不复杂:

构建状态流转图的逻辑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func buildFlowTaskFSM(r *runner, t flow.Task) *fsm.FSM {
 m := fsm.New(fsm.Option{
  Obj:    t,
  Logger: r.logger.With(fmt.Sprintf("task.%s.fsm", t.Name())),
 })

 m.From([]fsm.Status{flow.InitializingStatus}).
  To(flow.RunningStatus).
  When(flow.TaskTriggerEvent).
  Do(r.handleTaskRun)

 m.From([]fsm.Status{flow.RunningStatus}).
  To(flow.SucceedStatus).
  When(flow.TaskExecuteFinishEvent).
  Do(r.handleTaskSucceed)

 m.From([]fsm.Status{flow.RunningStatus}).
  To(flow.PausedStatus).
  When(flow.TaskExecutePauseEvent).
  Do(r.handleTaskPaused)

 m.From([]fsm.Status{flow.PausedStatus}).
  To(flow.RunningStatus).
  When(flow.TaskExecuteResumeEvent).
  Do(r.handleTaskResume)

 m.From([]fsm.Status{flow.InitializingStatus, flow.RunningStatus, flow.PausedStatus}).
  To(flow.CanceledStatus).
  When(flow.TaskExecuteCancelEvent).
  Do(r.handleTaskCanceled)

 m.From([]fsm.Status{flow.InitializingStatus, flow.RunningStatus}).
  To(flow.FailedStatus).
  When(flow.TaskExecuteErrorEvent).
  Do(r.handleTaskFailed)

 return m
}

状态流转逻辑与触发动作

状态的流转是 FSM 的核心,GoFlow 的状态流转,基本流程:

  1. 根据当前事件获取该事件影响的边
  2. 基于边的初始状态与当前状态匹配
  3. 如果存在匹配的边,则把当前状态机状态置为下级状态
  4. 状态转换完成后,执行对应的动作
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (m *FSM) Event(event Event) error {
 m.mux.Lock()
 defer m.mux.Unlock()

 m.logger.Debugf("handler fsm event: %s", event.Type)
 head := m.graph[event.Type]
 if head == nil {
  return nil
 }

 defer func() {
  if panicErr := recover(); panicErr != nil {
   m.logger.Errorf("event %s handle panic: %v", event, panicErr)
  }
 }()

 for head != nil {
  if m.obj.GetStatus() == head.from {
   m.logger.Infof("change obj status from %s to %s with event: %s", head.from, head.to, event.Type)
   m.obj.SetStatus(head.to)
   if event.Message != "" {
    m.obj.SetMessage(event.Message)
   }

   if head.do != nil {
    if handleErr := head.do(event); handleErr != nil {
     m.logger.Errorf("event %s handle failed: %s", event.Type, handleErr.Error())
     return handleErr
    }
   }
   return nil
  }
  head = head.next
 }
 return fmt.Errorf("get event %s and current status is %s, no change path matched", event.Type, m.obj.GetStatus())
}

有状态的状态机

GoFlow 的状态机是一种非常简单或者说偷懒的实现,主要问题在于这是一个有状态的状态机。听起来很奇怪,状态机本来不就是用来维护状态的吗。这里的状态其实指的是状态机本身的状态。简单来说,当 FSM 被实例化之后,这个用来维护任务的状态的 FSM 只能在当前进程(或者说副本)中使用,因为其他副本并没有这个 FSM 实例对象。

对于一个长时间运行的任务引擎来说,这件事情本身也无可厚非,但是换个场景,比如一个电商订单被创建之后,状态机实例也随之创建。如果这个订单只能被创建该订单的副本进程执行,其他副本均无法处理该订单,这是不可接受的。

状态机的无状态化有两种实现,一种是“集中处理”的方式,一种是“用时构造”的方式。

“集中处理”适合长尾任务,比如任务引擎,一个状态机是长时间存在的。该方式让多个副本通过类选主,选择出一个特定的副本来实例化 FSM,当其他的副本需要执行状态流转时,通过 RPC 或者事件的方式让持有 FSM 的副本处理。基于事件的实现方式是最优的,毕竟状态机本身也算是一个事件驱动的模式。这种设计的好处是简单、容易理解,当然坏处也比较明显,依赖了分布式的消息组件。

“用时构造”适合短时任务,比如电商订单的 API 请求,状态机只有在用的时候才需要存在。该方式是状态机不再维持当前状态,而是只存储状态流转图,在需要状态流转时,根据当前的实时状态重建状态机,在状态流转结束后,对状态机进行销毁。

最后

状态机本身并不是一个复杂的模型,甚至说得上简单。但是基于状态机延伸出的附加条件比较繁琐,比如状态嵌套、状态并行等,很多开源项目覆盖了的场景更全面,但也导致其实现十分复杂和效率低下。繁重的实现反而阻碍了在小场景的使用状态机的勇气。毕竟用 NASA 的方法论去制造一个木板凳是个很怪异的事情。所以不如尝试放手一搏,写一个贴近自己场景的 FSM。劝君上当,上当一回。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CS实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
有限状态机模型
在阅读harbor源码时,在jobservice代码中,发现实现了一个有限状态机。状态管理在系统设计中常被使用。通过实现它,可以方便的对程序的状态进行管理。状态在现实生活中,有很多存在的例子。例如,灯有开,关两种状态,当然如果较真的话,中间还可以有多个亮度的状态。红绿灯,登录状态,程序的生命周期等等,这个太多了。
暮雨
2018/11/15
1.7K0
有限状态机模型
有限状态机FSM的原理与GO的实现
有限状态机(Finite-state machine, 简写FSM)又可以称作有限状态自动机。它必须是可以附着在某种事物上的,且该事物的状态是有限的,通过某些触发事件,会让其状态发生转换。为此,有限状态机就是描述这些有限的状态和触发事件及转换行为的数学模型。 有限状态机组成 有限状态机有两个必要的特点,一是离散的,二是有限的。基于这两点,现实世界上绝大多数事物因为复杂的状态而无法用有限状态机表示。 而描述事物的有限状态机模型的元素由以下组成: 状态(State):事物的状态,包括初始状态和所有事件触发后的状
李海彬
2018/03/28
3.6K0
有限状态机FSM的原理与GO的实现
超轻量级有限状态机Mini-FSM
有限状态机(Finite State Machine,简称FSM),表示有限个状态以及在这些状态之间的转移和动作等行为的处理模型。在任何给定的时间点,有限状态机都处于某一特定状态,并且可以根据当前状态和输入条件,从当前状态转移到另一个状态。有限状态机相关的核心概念主要包括:
Yiwenwu
2024/05/18
8800
超轻量级有限状态机Mini-FSM
关于有限状态机(FSM)的一些思考
有限状态机,英文翻译是 Finite State Machine,缩写为 FSM,简称为状态机。状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。动作也不是必须的,也可能只转移状态,不执行任何动作。
巫山老妖
2023/09/24
2.3K0
关于有限状态机(FSM)的一些思考
JavaScript与有限状态机
有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物。 简单说,它有三个特征:   * 状态总数(state)是有限的。   * 任一时刻,只处在一
ruanyf
2018/04/12
9920
JavaScript与有限状态机
一个用go实现的有限状态机
不是特别喜欢,每次实例化fsm都需要重新传递对应events(虽然我们可以统一封装),我更期望在项目启动时把此项目涉及到不同业务状态机流转注册到fsm,对应:不同业务->[状态]->[事件]->处理事件主体(包含handler、params、hooks、observers等)。
RememberGo
2022/03/12
2.8K0
前端:从状态管理到有限状态机的思考
在使用类似Vue,React框架时,我们一定会使用状态管理吗?这个答案是肯定的。或许我不会主动去使用Vuex, Redux,但我们编写每一个组件的时候就已经在管理状态,Vuex, Redux只是更方便我们进行全局的状态管理。
coder_koala
2021/05/28
2.5K0
前端:从状态管理到有限状态机的思考
fsm:有限状态机
有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
一个会写诗的程序员
2020/09/28
1.5K0
fsm:有限状态机
基于 Kotlin 特性开发的有限状态机
状态机是古老的计算机理论,在游戏开发、嵌入式开发、网络协议等领域,得到广泛地使用。
fengzhizi715
2020/02/25
1.5K0
基于 Kotlin 特性开发的有限状态机
AI 助力游戏开发实践-有限状态机
在数字娱乐产业中,游戏开发无疑是最具活力和创新性的领域之一。随着技术的进步和玩家需求的日益增长,游戏开发者面临着前所未有的挑战和机遇。游戏不仅要在图形和玩法上不断创新,还要提供流畅的用户体验和智能的游戏逻辑。在这样的背景下,有限状态机(FSM)成为了游戏开发中一个不可或缺的工具,它为游戏角色和系统的行为提供了一种清晰、可控的实现方式。
腾讯云开发者
2025/01/09
2480
AI 助力游戏开发实践-有限状态机
‘SHIT’上最全有限状态机设计研究(一)-状态机介绍
本系列主要针对有限状态机进行分析和设计,其中主要包括两部分:时钟同步状态机的分析和时钟同步状态机的设计,预计会有五篇文章进行展开,其中介绍一篇,分析和设计分别有两篇,每一部分都会有相应的实例。
碎碎思
2020/06/30
1.4K0
有限状态机抽取地址
有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
DC童生
2019/07/26
1K0
有限状态机抽取地址
实战并发-使用分布式缓存和有限状态机
这里的并发不是高并发,只是将正式环境的一小段流量同时打到我的自测环境。一个请求同时多次发送,真正意义上并发处理同一个数据,主要需求是保证数据幂等性和正确性。
静儿
2019/03/19
1K0
实战并发-使用分布式缓存和有限状态机
AI 助力游戏开发实践-有限状态机
本文用实际案例讲述了腾讯 AI 代码助手是如何在 FSM 的开发过程中提供了高效的代码补全、优化和知识共享支持,显著提升了开发效率和代码质量的。
腾讯云代码助手
2025/01/08
830
一个有限状态机的C++实现
先放一个用C++11实现的FSM的代码: kuafu 咱们先来看一下什么是有限状态机(Finite-state machine, FSM), 先给一个 百度百科的解释 简单说就是作一件事可能会经
扫帚的影子
2018/09/05
3.6K0
一个有限状态机的C++实现
如何以面向对象的思想设计有限状态机
有限状态机又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型,用英文缩写也被简称为 FSM。
wenzid
2020/05/03
1.4K0
如何以面向对象的思想设计有限状态机
大中台模式下如何构建复杂业务核心状态机组件
由此可见,对于复杂状态的管理是一个业务依赖,需求多变的场景。在公司初创期,可以采用硬编码方式,对于每一个操作进行状态判断,每一步操作定制一套逻辑链路。随着业务的增加,定制化链路显然不优雅,大量流程代码无法维护,此时中台通用解决思路就尤为重要,有限状态机(Finite State Machine,缩写:FSM)开始在中台落地。
玄姐谈AGI
2019/11/07
2.7K0
大中台模式下如何构建复杂业务核心状态机组件
笨办法学 Python · 续 练习 30:有限状态机
每当你阅读一本关于解析的书,都有一个可怕的章节,关于有限状态机(FSM)。他们对“边”和“节点”进行了详细的分析,每个可能的“自动机”的组合被转换成其他自动机,坦率地说,它有点多了。FSM 有一个更简单的解释,使得它们实用并且可理解,而不会违背相同主题的纯理论版本。当然你不会向 ACM 提交论文,因为你不知道 FSM 背后的所有数学知识,但如果你只想在应用程序中使用它们,那么它们就足够简单了。
ApacheCN_飞龙
2022/12/01
5190
(有限)状态机
而我们常常说的状态机指有限状态机,缩写是FSM(Finite State Machine)。
滚神大人
2021/09/10
2.4K0
如何用有限状态机识别地址的有效性?
在收发快递填写地址的时候,我们会经常手动输入地址让程序智能识别,标准的地址比如,xx省xx市xx县/区xx路xx号,不过有时候也可以简单写:xx市xx县/区xx路xx号,或者xx省xx县/区xx路xx号,或者xx市xx路xx号。
somenzz
2021/09/14
5540
相关推荐
有限状态机模型
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文