找回密码
 立即注册
首页 业界区 安全 推荐一款高性能状态机管理解决方案

推荐一款高性能状态机管理解决方案

啦汇 2025-8-15 21:02:46
在实际软件开发中,尤其是工业软件,每一款设备都有复杂的状态以及状态之间的切换的功能需求,在这种情况下,如何管理状态以及状态之间切换,和对应状态下的功能控制,成为非常重要的一个问题。如果处理不好,那这种繁复的状态将成为“像面条一样”缠绕耦合,一团乱麻,真的就是“剪不断,理还乱”。那如何解决这个问题呢?今天我们以一篇简单的小例子,简述如何通过Stateless组件,完成状态的管理和触发,仅供学习分享使用,如有不足之处,还请指正。
 
什么是状态模式?

 
关于状态模式和状态机,如下所示:

  • 状态模式:"允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类 "。(State Pattern: "Allow an object to alter its behavior when its internal state changes. The object will appear to change its class ".)。对于这个定义,有点抽象,变通理解一下可以这么理解:状态拥有者将变更行为委托给状态对象,状态拥有者本身只拥有状态(当然也可以抛弃状态对象),状态对象履行变更职责。
  • 状态机:"依照指定的状态流程图,根据当前执行的动作,将当前状态按照预定的条件变更到新的状态 "。状态机有4个要素,即现态、条件、动作、次态。其中,现态和条件是“因”, 动作和次态是“果”。




    • 现态 - 是指当前对象的状态
    • 条件 - 当一个条件满足时,当前对象会触发一个动作
    • 动作 - 条件满足之后,执行的动作
    • 次态 - 条件满足之后,当前对象的新状态。次态是相对现态而言的,次态一旦触发,就变成了现态



  • 状态迁移图:"在UML建模中,常常可见,用来描述一个特定的对象所有可能的状态,以及由于各种事件的发生而引起的状态之间的转移和变化,也是配置状态机按照何种行径的前提 "。
 
什么是Stateless?

 
Stateless是一个轻量级、高性能的状态机库,它基于.Net Standard实现,在.Net Framework和.Net Core项目中都可以使用,能够轻松地帮助我们实现状态转换的逻辑。
github:https://github.com/dotnet-state-machine/stateless
1.png

 
实例场景说明

 
本文主要通过Stateless组件实现如下一个状态的管理和触发,
当前有一个设备软件,它拥有如下几个状态(State):

  • Idle状态,表示当前没有运行任务,处于空闲状态。
  • Running状态,表示当前正在工作,处于运行状态。
  • Malfunction状态,表示当前有异常,出于故障状态。
  • Recovery状态,表示经过维修,从故障中恢复了,处于修复状态。
它拥有触发动作(Trigger):

  • Work,表示当前开始工作,它可以由Idle状态进入,也可以从Recovery状态进入,程序会进入Running状态。
  • Fatal,表示遇到异常,它只可以由Running状态进入,程序会进入Malfunction状态。
  • Repair,表示修复异常,它只可以由Malfunction状态进入,程序会进入Recovery状态。
  • Finish,表示完成,它可以由Running状态进入,也可以由Recovery状态进入,程序会进入Idle状态。
上述状态(State)和触发动作(Trigger)以及之间的流转,如下图所示:
2.png

 
创建项目

 
为了演示状态变化,我们创建一个WinForm应用程序“Okcoder.Stateless.WinTool”和一个公共库“Okcoder.Stateless.Common”,如下所示:
3.png

 
安装Stateless组件

 
在Visual Studio 2022开发工具中,可以通过Nuget包管理器进行安装。在解决方案右键,打开右键菜单,然后点击“管理解决方案的Nuget程序包”在打开的Nuget包解决方案,搜索Stateless,然后安装即可。当前最新版为v5.18.0,如下所示:
4.png

 
定义状态和触发器

 
状态机管理状态的变化和触发事件,所以定义状态和触发器,它们是枚举类型,根据实例场景说明进行定义ToolState如下所示:
  1. namespace Okcoder.Stateless.Common
  2. {
  3.     /// <summary>
  4.     /// 工具状态
  5.     /// </summary>
  6.     public enum ToolState
  7.     {
  8.         Idle=0, //空闲状态
  9.         Running=1, //运行状态
  10.         Malfunction = 2, // 故障状态
  11.         Recovery=3, //已恢复状态
  12.     }
  13. }
复制代码
ToolTrigger定义如下所示:
  1. namespace Okcoder.Stateless.Common
  2. {
  3.     public enum ToolTrigger
  4.     {
  5.         Work = 0, // 开始运转
  6.         Fatal = 1, // 出异常
  7.         Finish = 2, // 完成
  8.         Repair=3, //维修
  9.     }
  10. }
复制代码
 
定义接口

 
定义IToolStateService接口,它表示状态机所具备的能力,如触发动作,状态判断,状态机导出DotGraph等,如下所示:
  1. namespace Okcoder.Stateless.Common
  2. {
  3.     /// <summary>
  4.     /// 状态机接口
  5.     /// </summary>
  6.     public interface IToolStateService
  7.     {
  8.         /// <summary>
  9.         /// 初始化状态
  10.         /// </summary>
  11.         void Init();
  12.         /// <summary>
  13.         /// 开始工作
  14.         /// </summary>
  15.         /// <returns></returns>
  16.         ToolResult Work();
  17.         /// <summary>
  18.         /// 遇到严重错误
  19.         /// </summary>
  20.         /// <returns></returns>
  21.         ToolResult Fatal();
  22.         /// <summary>
  23.         /// 维修
  24.         /// </summary>
  25.         /// <returns></returns>
  26.         ToolResult Repair();
  27.         /// <summary>
  28.         /// 完成
  29.         /// </summary>
  30.         /// <returns></returns>
  31.         ToolResult Finish();
  32.         /// <summary>
  33.         /// 是否是Idle状态
  34.         /// </summary>
  35.         /// <returns></returns>
  36.         bool IsIdleState();
  37.         /// <summary>
  38.         /// 是否运行状态
  39.         /// </summary>
  40.         /// <returns></returns>
  41.         bool IsRunningState();
  42.         /// <summary>
  43.         /// 是否故障状态
  44.         /// </summary>
  45.         /// <returns></returns>
  46.         bool IsMalfunctionState();
  47.         /// <summary>
  48.         /// 是否恢复状态
  49.         /// </summary>
  50.         /// <returns></returns>
  51.         bool IsRecoveryState();
  52.         /// <summary>
  53.         /// 获取当前状态
  54.         /// </summary>
  55.         /// <returns></returns>
  56.         ToolState GetToolState();
  57.         /// <summary>
  58.         /// 状态机导出DotGraph
  59.         /// </summary>
  60.         /// <param name="path"></param>
  61.         void ExportDotGraph(string path);
  62.     }
  63. }
复制代码
定义设备工作IToolWorkService接口,它表示设备运行工作,执行操作。如下所示:
  1. namespace Okcoder.Stateless.Common
  2. {
  3.     /// <summary>
  4.     /// 设备工作流程接口
  5.     /// </summary>
  6.     public interface IToolWorkService
  7.     {
  8.         /// <summary>
  9.         /// 是否制造故障
  10.         /// </summary>
  11.         bool IsMakeFatal { get; set; }
  12.         /// <summary>
  13.         /// 开始工作
  14.         /// </summary>
  15.         void DoWork();
  16.         /// <summary>
  17.         /// 维修
  18.         /// </summary>
  19.         void Repair();
  20.     }
  21. }
复制代码
 
实现服务

 
实现状态机服务ToolStateService,它主要应用Stateless组件实现状态的管理和触发动作切换状态,如下所示:
  1. using Stateless;
  2. using Stateless.Graph;
  3. using System.Text;
  4. namespace Okcoder.Stateless.Common
  5. {
  6.         /// <summary>
  7.         /// 状态机服务
  8.         /// </summary>
  9.         public class ToolStateService : IToolStateService
  10.         {
  11.                 private Action<ToolState> toolAction;// 动作
  12.         private StateMachine<ToolState, ToolTrigger> toolStateMachine;
  13.                 public ToolStateService(Action<ToolState> toolAction)
  14.                 {
  15.                         //定义状态机
  16.                         this.toolStateMachine = new StateMachine<ToolState, ToolTrigger>(ToolState.Idle);
  17.                         //配置每一个状态所允许的动作
  18.                         this.toolStateMachine.Configure(ToolState.Idle).Permit(ToolTrigger.Work, ToolState.Running);
  19.                         this.toolStateMachine.Configure(ToolState.Running).Permit(ToolTrigger.Fatal, ToolState.Malfunction).Permit(ToolTrigger.Finish, ToolState.Idle);
  20.                         this.toolStateMachine.Configure(ToolState.Malfunction).Permit(ToolTrigger.Repair, ToolState.Recovery);
  21.                         this.toolStateMachine.Configure(ToolState.Recovery).Permit(ToolTrigger.Work, ToolState.Running).Permit(ToolTrigger.Finish, ToolState.Idle);
  22.                         this.toolAction = toolAction;
  23.                        
  24.                 }
  25.                 /// <summary>
  26.                 /// 初始化状态
  27.                 /// </summary>
  28.                 public void Init()
  29.                 {
  30.                         this.toolAction(ToolState.Idle);
  31.                 }
  32.                 /// <summary>
  33.                 /// 响应触发动作
  34.                 /// </summary>
  35.                 /// <param name="trigger"></param>
  36.                 private ToolResult Fire(ToolTrigger trigger)
  37.                 {
  38.                         ToolResult toolResult = new ToolResult();
  39.                         try
  40.                         {
  41.                                 if (!this.toolStateMachine.CanFire(trigger))
  42.                                 {
  43.                                         toolResult.IsOk = false;
  44.                                         toolResult.Desc = $"当前状态是{this.toolStateMachine.State},不允许执行{trigger}操作";
  45.                                 }
  46.                                 else
  47.                                 {
  48.                                         this.toolStateMachine.Fire(trigger);
  49.                                         toolResult.IsOk = true;
  50.                                         toolResult.Desc = "Ok";
  51.                                         DoAction();
  52.                 }
  53.                         }
  54.                         catch (InvalidOperationException ex)
  55.                         {
  56.                                 toolResult.IsOk = false;
  57.                                 toolResult.Desc = ex.Message;
  58.                         }
  59.                         return toolResult;
  60.                 }
  61.                 /// <summary>
  62.                 /// 判断是否在状态
  63.                 /// </summary>
  64.                 /// <param name="toolState"></param>
  65.                 /// <returns></returns>
  66.                 private bool IsInState(ToolState toolState)
  67.                 {
  68.                         return this.toolStateMachine.IsInState(toolState);
  69.                 }
  70.                 /// <summary>
  71.                 /// 事件通知
  72.                 /// </summary>
  73.                 private void DoAction()
  74.                 {
  75.                         if (this.toolAction != null)
  76.                         {
  77.                                 this.toolAction(this.toolStateMachine.State);
  78.                         }
  79.                 }
  80.                 /// <summary>
  81.                 /// 开始工作
  82.                 /// </summary>
  83.                 public ToolResult Work()
  84.                 {
  85.                         return this.Fire(ToolTrigger.Work);
  86.                 }
  87.                 /// <summary>
  88.                 /// 遇到严重错误
  89.                 /// </summary>
  90.                 public ToolResult Fatal()
  91.                 {
  92.                         return this.Fire(ToolTrigger.Fatal);
  93.                 }
  94.                 /// <summary>
  95.                 /// 维修
  96.                 /// </summary>
  97.                 public ToolResult Repair()
  98.                 {
  99.                         return this.Fire(ToolTrigger.Repair);
  100.                 }
  101.                 /// <summary>
  102.                 /// 完成
  103.                 /// </summary>
  104.                 public ToolResult Finish()
  105.                 {
  106.                         return this.Fire(ToolTrigger.Finish);
  107.                 }
  108.                 /// <summary>
  109.                 /// 是否是Idle状态
  110.                 /// </summary>
  111.                 /// <returns></returns>
  112.                 public bool IsIdleState()
  113.                 {
  114.                         return this.IsInState(ToolState.Idle);
  115.                 }
  116.                 /// <summary>
  117.                 /// 是否运行状态
  118.                 /// </summary>
  119.                 /// <returns></returns>
  120.                 public bool IsRunningState()
  121.                 {
  122.                         return this.IsInState(ToolState.Running);
  123.                 }
  124.                 /// <summary>
  125.                 /// 是否故障状态
  126.                 /// </summary>
  127.                 /// <returns></returns>
  128.                 public bool IsMalfunctionState()
  129.                 {
  130.                         return this.IsInState(ToolState.Malfunction);
  131.                 }
  132.                 /// <summary>
  133.                 /// 是否恢复状态
  134.                 /// </summary>
  135.                 /// <returns></returns>
  136.                 public bool IsRecoveryState()
  137.                 {
  138.                         return this.IsInState(ToolState.Recovery);
  139.                 }
  140.                 public ToolState GetToolState()
  141.                 {
  142.                         return this.toolStateMachine.State;
  143.                 }
  144.                 /// <summary>
  145.                 /// 状态机导出DotGraph
  146.                 /// </summary>
  147.                 /// <param name="path"></param>
  148.                 public void ExportDotGraph(string path)
  149.                 {
  150.                         var info = this.toolStateMachine.GetInfo();
  151.                         string graph = UmlDotGraph.Format(info);
  152.                         using (FileStream stream = File.OpenWrite(path))
  153.                         {
  154.                                 using (StreamWriter writer = new StreamWriter(stream))
  155.                                 {
  156.                                         writer.Write(graph);
  157.                                         writer.Close();
  158.                                 }
  159.                                 stream.Close();
  160.                         }
  161.                 }
  162.         }
  163. }
复制代码
其中StateMachine是状态机的核心类,它有两个泛型参数,分别表示ToolState和ToolTrigger,构造函数中传入初始状态,在本示例中,传入ToolStage.Idle即可。
StateMachine通过对象实例的Configure方法配置允许的状态及通过Permit方法配置当前状态允许通过Trigger转换的新的状态。
配置完成后,通过Fire方法响应触发动作,通过IsInState方法判断当前是否处于指定的状态。
在状态机服务类构造函数中,接收一个Action的委托方法,用于输出状态到UI层,当然也可以采用其他事件订阅发布的解耦方法。
其中ToolResult返回值,表示状态机执行后返回的接口,它是一个类,如下所示:
  1. namespace Okcoder.Stateless.Common
  2. {
  3.     /// <summary>
  4.     /// 工具执行结果
  5.     /// </summary>
  6.     public class ToolResult
  7.     {
  8.         public bool IsOk { get; set; }
  9.         public string Desc { get; set; }
  10.     }
  11. }
复制代码
 
 实现工作服务ToolWorkService,主要是模拟设备工具运行,当开始工作时,输出文本到UI页面上,如果有异常,则停止;若无异常,则运行完成,如下所示:
  1. namespace Okcoder.Stateless.Common
  2. {
  3.     /// <summary>
  4.     /// 设备工作服务
  5.     /// </summary>
  6.     public class ToolWorkService:IToolWorkService
  7.     {
  8.         private Action<string> _progress;
  9.         private IToolStateService _toolStateService;
  10.         private bool _isMakeFatal=false;
  11.         public bool IsMakeFatal
  12.         {
  13.             get { return _isMakeFatal; }
  14.             set { _isMakeFatal = value; }
  15.         }
  16.         public ToolWorkService(IToolStateService toolStateService, Action<string> progress,bool isMakeFatal=false)
  17.         {
  18.             _progress = progress;
  19.             _toolStateService = toolStateService;
  20.             _isMakeFatal = isMakeFatal;
  21.         }
  22.         public void DoWork()
  23.         {
  24.             BeginWork();
  25.             Work();
  26.             Finish();
  27.         }
  28.         public void Repair()
  29.         {
  30.             var result = this._toolStateService.Repair();
  31.             if (result.IsOk)
  32.             {
  33.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 故障被修好了");
  34.             }
  35.             else
  36.             {
  37.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 故障没有被修好");
  38.             }
  39.             
  40.         }
  41.         /// <summary>
  42.         /// 开始工作
  43.         /// </summary>
  44.         private void BeginWork()
  45.         {
  46.             var result = this._toolStateService.Work();
  47.             if (result.IsOk)
  48.             {
  49.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 开始工作了");
  50.             }
  51.             else
  52.             {
  53.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 开始工作失败了");
  54.             }
  55.             
  56.         }
  57.         /// <summary>
  58.         /// 工作过程
  59.         /// </summary>
  60.         private void Work()
  61.         {
  62.             for (int i = 0; i < 30; i++)
  63.             {
  64.                 if (_isMakeFatal)
  65.                 {
  66.                     if (i > 10)
  67.                     {
  68.                         MarkFatal();// 运行一段时间,报故障
  69.                         break;
  70.                     }
  71.                 }
  72.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 当前正在工作 - {i}");
  73.                 Thread.Sleep(300);
  74.             }
  75.         }
  76.         /// <summary>
  77.         /// 完成工作
  78.         /// </summary>
  79.         private void Finish()
  80.         {
  81.             var result = this._toolStateService.Finish();
  82.             if (result.IsOk)
  83.             {
  84.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 完成工作了");
  85.             }
  86.             else
  87.             {
  88.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {result.Desc}");
  89.             }
  90.         }
  91.         private void MarkFatal()
  92.         {
  93.             var result = this._toolStateService.Fatal();
  94.             if (result.IsOk)
  95.             {
  96.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 哎呀,出错了呢?");
  97.             }
  98.             else
  99.             {
  100.                 OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 制造故障失败");
  101.             }
  102.             
  103.         }
  104.         /// <summary>
  105.         /// 输出信息
  106.         /// </summary>
  107.         /// <param name="msg"></param>
  108.         private void OutPutInfo(string msg)
  109.         {
  110.             if (_progress != null)
  111.             {
  112.                 _progress(msg);
  113.             }
  114.         }
  115.     }
  116. }
复制代码
其中构造函数接收一个Action的委托,用于向UI输出信息,当然也可以是其他的形式。
 
UI调用

 
在Okcoder.Stateless.WinTool项目中,创建FrmMain页面,在构造函数中定义IToolStateService和IToolWorkService的对象实例,并且在Load方法中初始化。
当用户点击Work时,开始调用IToolWorkService的Work方法,在执行过程中,会输出信息到UI页面。
在UI页面有一个复选框,用于设置是否触发异常,可以模拟有异常的场景,如果出现异常,则点击Repair按钮进行修复。
在执行各个动作时,可以看到不同的状态变化。如下所示:
  1. using Okcoder.Stateless.Common;
  2. namespace Okcoder.Stateless.WinTool
  3. {
  4.     public partial class FrmMain : Form
  5.     {
  6.         private IToolStateService toolStateService;
  7.         private IToolWorkService toolWorkService;
  8.         public FrmMain()
  9.         {
  10.             InitializeComponent();
  11.             this.toolStateService = new ToolStateService(ToolStateAction);
  12.             this.toolWorkService = new ToolWorkService(toolStateService, ToolWorkProcessAction, this.chkFatal.Checked);
  13.         }
  14.         private void FrmMain_Load(object sender, EventArgs e)
  15.         {
  16.             this.toolStateService.Init();
  17.         }
  18.         /// <summary>
  19.         /// 开始工作
  20.         /// </summary>
  21.         /// <param name="sender"></param>
  22.         /// <param name="e"></param>
  23.         private void btnWork_Click(object sender, EventArgs e)
  24.         {
  25.             this.txtInfo.Text = string.Empty;
  26.             this.toolWorkService.IsMakeFatal = this.chkFatal.Checked;
  27.             Task.Run(() =>
  28.             {
  29.                 this.toolWorkService.DoWork();
  30.             });
  31.         }
  32.         /// <summary>
  33.         /// 维修
  34.         /// </summary>
  35.         /// <param name="sender"></param>
  36.         /// <param name="e"></param>
  37.         private void btnRepair_Click(object sender, EventArgs e)
  38.         {
  39.             this.toolWorkService.Repair();
  40.         }
  41.         /// <summary>
  42.         /// 导出状态机DotGraph文件
  43.         /// </summary>
  44.         /// <param name="sender"></param>
  45.         /// <param name="e"></param>
  46.         private void btnExport_Click(object sender, EventArgs e)
  47.         {
  48.             SaveFileDialog saveFileDialog = new SaveFileDialog();
  49.             saveFileDialog.DefaultExt = "txt";
  50.             saveFileDialog.Filter = "txt文件|*.txt";
  51.             saveFileDialog.FileName = "Okcoder.txt";
  52.             if (saveFileDialog.ShowDialog() == DialogResult.OK)
  53.             {
  54.                 var filePath = saveFileDialog.FileName;
  55.                 this.toolStateService.ExportDotGraph(filePath);
  56.                 MessageBox.Show("导出成功");
  57.             }
  58.         }
  59.         private void ToolStateAction(ToolState toolState)
  60.         {
  61.             this.Invoke(() =>
  62.             {
  63.                 this.lblState.Text = toolState.ToString();
  64.                 switch (toolState)
  65.                 {
  66.                     case ToolState.Idle:
  67.                     case ToolState.Recovery:
  68.                         this.btnWork.Enabled = true;
  69.                         this.btnRepair.Enabled = false;
  70.                         this.lblState.ForeColor = Color.Black;
  71.                         break;
  72.                     case ToolState.Running:
  73.                         this.btnWork.Enabled = false;
  74.                         this.btnRepair.Enabled = false;
  75.                         this.lblState.ForeColor = Color.Goldenrod;
  76.                         break;
  77.                     case ToolState.Malfunction:
  78.                         this.btnWork.Enabled = false;
  79.                         this.btnRepair.Enabled = true;
  80.                         this.lblState.ForeColor = Color.Red;
  81.                         break;
  82.                 }
  83.                 this.Refresh();
  84.             });
  85.         }
  86.         private void ToolWorkProcessAction(string msg)
  87.         {
  88.             this.Invoke(() =>
  89.             {
  90.                 this.txtInfo.AppendText(msg + "\r\n");
  91.             });
  92.         }
  93.     }
  94. }
复制代码
 
实例演示

 
经过上述步骤,运行程序,然后点击Work按钮,默认无故障时如下所示:
5.gif

当勾选故障,模拟出现故障时,如下所示:
6.gif

以上就是《推荐一款高性能状态机管理解决方案》,旨在抛砖引玉,一起学习,共同进步。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册