c++状态机的使用

简介: c++状态机的使用

什么是状态机


状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。英文名字叫State Machine ,不是指一台实际机器,一般就是指一张状态转换图。全称是有限状态自动机,自动两个字包含重要含义。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的,当输入条件时,能输出下一个状态。


现实事物是有不同状态,例如一个LED等,就有 亮 和 灭两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如LED灯的状态就是两个亮和 灭。



为什么用状态机


状态机解决的问题就是当某种模型的状态变更比较比较复杂,且状态比较多,那么我们有必要将这些状态变化的逻辑抽象出来,做成一个可以统一调用的算法,这样封装出来的代码就比较好维护,同时可读性也很强。


状态机在实际工作开发中很有用,应用也非常广泛。一个健壮的状态机可以让你的程序,不论发生何种突发事件都不会突然进入一个不可预知的程序分支,可以很清晰的表达整个状态的流转。


在GUI应用程序、Web应用程序等事件驱动型的应用程序,采用状态机的思路来完成程序设计,可以简化设计流程,使程序的可读性、可维护性都得到增加。


使用状态机有哪些好处?


1. 当一个程序有多个状态时,规范了状态机的状态转换,避免了一些引入一些复杂的判断逻辑。


2. 规范了程序在不同状态下所能提供的能力。


3. 在能力上可以进行横向扩展,提供新的状态来完善现有逻辑。


简单状态机的实现


使用switch跳转即可实现一种简单的状态机。如果逻辑不是很复杂,使用switch语句也能达到实现目的。举例如下:


enum state
  {
    nullState_,
    firstState_,
    secondState_,
    thirdState_,
    quitState_,
  };
struct param_t
  {
    int param1;
    int param2;
// ......
  };
int nullStateProc(param_t& param){
    return firstState_;
}
int firstStateProc(param_t& param){
    return secondState_;
}
int secondStateProc(param_t& param){
    return secondState_;
}
int thirdStateProc(param_t& param){
    return secondState_;
}
int quitEvent(){
    return nullState_;
}
int stateMachine(state& state_, param_t& param){
    switch (state_)
  {
    case nullState_:
      state_ = static_cast<state>(nullStateProc(param));
      break;
    case firstState_:
      state_ = static_cast<state>(firstStateProc(param));
      break;
    case secondState_:
      state_ = static_cast<state>(secondStateProc(param));
      break;
    case thirdState_:
      state_ = static_cast<state>(thirdStateProc(param));
      break;
    case quitState_:
      quitEvent();
      return 0;
  }
  return 1;
}
void start()
{
  state state_ = nullState_;
  param_t param{};
  while (true)
  {
    auto stateResult = stateMachine(state_, param);
    if (!stateResult)
    {
      return;
    }
  }
}


另一种简单实现


如果需要管理的状态和事件比较多,需要逻辑清晰和便于维护,使用简单的switch可能无法满足需求。这里介绍一种简单的实现,消除庞大的条件分支语句,从配置表容易看出各个状态的转换图。


/*  statemachine.h*/
#ifndef _STATEMACHINE_H_
#define _STATEMACHINE_H_
#include <iostream>
enum EventActionResult {
    EventFailed, EventProcessedOK
};
template<class T, class P>
class State {
public:
    std::string inputEvent;
    State<T, P> *nextState;
    EventActionResult (T::*action)(const std::string &event, P *param);
    State<T, P> *errorState;
};
template<class T, class P>
class StateMachine {
private:
    State<T, P> *init;
    State<T, P> *current;
    T *target;
public:
    StateMachine() {}
    void Init(T *_target, State<T, P> *initialState) {
        init = current = initialState;
        target = _target;
    }
    void Reset() {
        current = init;
    }
    void ProcessEvent(const std::string &event, P *param) {
        for (State<T, P> *p = this->current; p->nextState != NULL; p++) {
            if (p->inputEvent == event) {
                if (p->action != NULL) {
                    if (EventFailed == (this->target->*(p->action))(event, param)) {
                        if (p->errorState != NULL) {
                            //Only if there's an errorstate defined. Otherwise, just do nothing
                            this->current = p->errorState;
                        }
                        return;
                    }
                }
                this->current = p->nextState;
                return;
            }
        }
        //Event not found. Do nothing
        return;
    }
};


以下是使用举例:


class MyStateMachine {
public:
    struct param_t {
        int param;
        int param1;
        int param2;
    };
    StateMachine<MyStateMachine, param_t> stMachine;
    EventActionResult HandleEvent1(const std::string &e, param_t *param);
    EventActionResult HandleEvent2(const std::string &e, param_t *param);
    EventActionResult HandleEventA(const std::string &e, param_t *param);
    EventActionResult HandleEventB(const std::string &e, param_t *param);
    EventActionResult HandleThree(const std::string &e, param_t *param);
    void HandleEvent(const std::string &e, param_t *param);
    void Init();
    void Start();
};


#include "statemachine.h"
#include <cstdlib>
#include <iostream>
#include <stdio.h>
typedef State<MyStateMachine, MyStateMachine::param_t> STATE;
extern STATE Idle[];
extern STATE One[];
extern STATE Two[];
STATE Idle[] =
        {
                //EVENT,NEXT,  ACTION,   ERRORSTATE (where to land if there's an error)
                {"event1", One, &MyStateMachine::HandleEvent1, Idle},
                {"event2", Two, &MyStateMachine::HandleEvent2, Idle},
                {"", NULL, NULL, NULL}, //End of table
        };
STATE One[] =
        {
                {"eventA", Idle, &MyStateMachine::HandleEventA, Idle},
                {"eventB", Idle, &MyStateMachine::HandleEventB, Idle},
                {"", NULL, NULL, NULL},
        };
STATE Two[] =
        {
                {"eventC", Idle, NULL, NULL},
                {"", NULL,  NULL, NULL},
        };
EventActionResult MyStateMachine::HandleEvent1(const std::string &e, param_t *param) {
    std::cout << "HandleEvent1,param:" << param->param << std::endl;
    return EventProcessedOK;
}
EventActionResult MyStateMachine::HandleEvent2(const std::string &e, param_t *param) {
    std::cout << "HandleEvent2,param:" << param->param << std::endl;
    return EventProcessedOK;
}
EventActionResult MyStateMachine::HandleEventA(const std::string &e, param_t *param) {
    std::cout << "HandleEventA,param:" << param->param << std::endl;
    return EventProcessedOK;
}
EventActionResult MyStateMachine::HandleEventB(const std::string &e, param_t *param) {
    std::cout << "HandleEventB,param:" << param->param << std::endl;
    return EventProcessedOK;
}
EventActionResult MyStateMachine::HandleThree(const std::string &e, param_t *param) {
    std::cout << "HandleThree" << std::endl;
    return EventProcessedOK;
}
void MyStateMachine::HandleEvent(const std::string &e, param_t *param) {
    stMachine.ProcessEvent(e, param);
}
void MyStateMachine::Init() {
    stMachine.Init(this, Idle);
}
void MyStateMachine::Start() {
    while (1) {
        char c[255];
        // 模拟输入event
        std::cin.getline(c,255);
        std::string event{c};
        MyStateMachine::param_t param;
        param.param = 1;
        this->HandleEvent(event, &param);
    }
}


以上示例在单线程下没问题,但若考虑多线程并发,考虑到当一个跳转正在进行的时候,同时又有其他任务请求跳转,则可能会出现数据不一致的问题。


举个例子:task1(s1, c1/a1 –> s2) 和 task2(s2, c2/a2 –> s3) 先后执行,是可以顺利到达s3状态的,但若操作a1运行的时候,执行权限被task2抢占,则task2此时看到的当前状态还是s1,s1遇到c2就进入陷阱状态,而不会到达s3了,也就是说,状态的跳转发生了不确定,这是不能容忍的。


因此要重新设计状态机,用上队列,把触发事件加入到队列中依次出队和执行。


#include <condition_variable>
#include <mutex>
#include <queue>
/** Multiple producer, multiple consumer thread safe queue
 * Since 'return by reference' is used this queue won't throw */
template <typename T>
class shared_queue
{
  std::queue<T> queue_;
  mutable std::mutex m_;
  std::condition_variable data_cond_;
public:
  shared_queue() = default;
  shared_queue &operator=(const shared_queue &) = delete;
  shared_queue(const shared_queue &other) = delete;
  void push(T item)
  {
    {
      std::lock_guard<std::mutex> lock(m_);
      queue_.push(std::move(item));
    }
    data_cond_.notify_one();
  }
  /// Try to retrieve, if no items, wait till an item is available and try again
  void wait_and_pop(T &popped_item)
  {
    std::unique_lock<std::mutex> lock(m_);
    while (queue_.empty())
    {
      data_cond_.wait(lock);
      //  This 'while' loop is equal to
      //  data_cond_.wait(lock, [](bool result){return !queue_.empty();});
    }
    popped_item = std::move(queue_.front());
    queue_.pop();
  }
  bool wait_and_pop_timed(T &popped_item, std::int64_t duration)
  {
    std::unique_lock<std::mutex> lock(m_);
    while (queue_.empty())
    {
      auto timeout = data_cond_.wait_for(lock, std::chrono::microseconds(duration));
      if (timeout == std::cv_status::timeout)
      {
        return true;
      }
    }
    popped_item = std::move(queue_.front());
    queue_.pop();
    return false;
  }
};


引用


什么是状态机?_pingxiaozhao的博客-CSDN博客_状态机的概念


为Linux应用构造有限状态机_wowocpp的博客-CSDN博客_linux 状态机


有限状态机详解(转载)_白小狮的博客-CSDN博客_有限状态机和无限状态机


Linux进程是如何创建出来的?


为Linux操作系统应用构造有限状态机方法-红联Linux系统门户


一文详解 Android状态机StateMachine 使用方式及实现原理_bjxiaxueliang的博客-CSDN博客_statemachine


c++写状态机_zhi_cary的博客-CSDN博客_c++ 状态机


C++有限状态机的实现_Valreaper的博客-CSDN博客_c++ 状态机


github经典C++状态机(fsm)源代码剖析_star-keke的博客-CSDN博客


用C++来实现有限状态机(附代码)_李肖遥的博客-CSDN博客


TinyFSM 介绍_百思可乐的博客-CSDN博客


C++状态机框架实现 - 灰信网(软件开发博客聚合)


状态模式(state)C++实现_shu_chang1993的博客-CSDN博客_c++state


c++写状态机_zhi_cary的博客-CSDN博客_c++ 状态机

相关文章
|
uml
状态机
首先需要考虑涉及到哪些状态节点和哪些事件,如何方便状态节点的获取、状态节点如何串联起来呢?串联的方式下,如何拿到下一个状态节点?如果基于角色,如何实现? 我们知道工作流可以实现基于角色进行流程的流转,但是此时我们涉及到事件和状态,会出现多个分支,如果使用工作流实现,流程处理上,比如activiti上,可能比较复杂,因此考虑比较轻量级的状态机来实现的话,相对来说要方便一些。
1075 0
状态机
|
4月前
|
设计模式 JavaScript 安全
MobX状态管理:简洁而强大的状态机
MobX 是一种简洁高效的状态管理库,通过声明式的方式管理应用状态,使数据变化自动同步到视图。它利用 `@observable` 创建响应式数据,`@computed` 实现自动更新的计算属性,`@action` 确保状态安全变更。结合 `mobx-react`,可在 React 组件中使用 `observer` 自动响应状态更新。MobX 内部采用代理与访问者模式追踪依赖,确保最小化更新,提升性能。支持 TypeScript,提供类型安全与代码提示。
72 2
|
5月前
|
测试技术
领域驱动设计问题之状态同步模型与状态机模型的主要区别是什么
领域驱动设计问题之状态同步模型与状态机模型的主要区别是什么
4 状态机
4 状态机
60 0
|
人工智能 安全 图形学
有限状态机的概念
有限状态机的概念
160 0
|
传感器 数据可视化 JavaScript
状态机(State Machines):理解、设计和应用有限状态机
状态机(State Machines)是一种强大的计算模型和设计工具,用于建模和控制有限状态的系统和行为。无论是在软件开发、自动化控制、游戏设计还是其他领域,状态机都发挥着关键作用。本博客将深入探讨状态机的概念、工作原理以及如何在不同应用中设计和应用它们。
5445 0
|
uml
UML——同步消息和异步消息的区别(顺序图中)
UML——同步消息和异步消息的区别(顺序图中)
1417 0
|
存储 算法 异构计算
状态机的概念与设计
⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
293 0
状态机的概念与设计
|
JavaScript 前端开发 API
Zag-基于状态机的组件库
本文适合对状态机感兴趣的小伙伴阅读
Zag-基于状态机的组件库
|
算法 网络协议 Java
状态机是干什么的?底层原理是什么?
状态机是干什么的?底层原理是什么?
671 0