3

我在一个实时系统中有一个状态机,只有很少 (3) 个状态。

typedef enum {
    STATE1,
    STATE2,
    STATE3
} state_t;

然而,这些状态之间的转换需要相当长的时间并且有自己的细分。所以我有两个选择,或者我扩展主状态机,以便表示所有中间状态:

typedef enum {
    STATE1,
    STATE1_PREPARE_TRANSITION_TO_STATE2,
    STATE1_DO_TRANSITION_TO_STATE2,
    STATE1_PREPARE_TRANSITION_TO_STATE3,
    STATE1_DO_TRANSITION_TO_STATE3,
    STATE2,
    ...
} state_t;

或者我为相关的主要状态创建一个嵌套状态机:

typedef enum {
    STATE1_NOT_ACTIVE,
    STATE1_NORMAL,
    STATE1_PREPARE_TRANSITION_TO_STATE2,
    STATE1_DO_TRANSITION_TO_STATE2,
    STATE1_PREPARE_TRANSITION_TO_STATE3,
    STATE1_DO_TRANSITION_TO_STATE3
} sub_state1_t;
...

这两种可能性都有其优点和缺点。大型状态机很容易变得混乱和复杂。然而,在第二种情况下使所有状态保持一致也不是微不足道的,许多函数都需要有关全局状态和子状态的信息。

我想避免必须处理多个并行状态的复杂代码,例如:

if ((global_state == STATE1) &&
    (sub_state_1 == STATE1_DO_TRANSITION_TO_STATE2))
{
    ...
    if (transition_xy_done(...))
    {
        global_state = STATE2;
        sub_state_1 = STATE1_NOT_ACTIVE;
        sub_state_2 = STATE2_NORMAL;
    }
}

解决此类问题的一般最佳方法是什么:许多小型嵌套状态机(具有许多无效组合)、一个大型状态机或其他任何东西?

4

6 回答 6

5

Many small state machines are going to give you more code flexibility down the road, especially if you need to redesign anything. Then you should (hopefully) be able to change a nested state machine without having to change any of the other nested state machines.

Having a bigger transition table shouldn't result in longer lookups, since I assume that you lay out the table sensibly in memory. If anything, you should actually be able to get a little more speed out of the big machine simply because you don't have the extra one or two steps you might need with the small state machines to transition cleanly between them. But given the added complexity of this method, I'd suggest the following: design with nested state machines, then once everything works, refactor into a single state machine if necessary to gain a little speed boost.

于 2009-09-10T10:55:21.780 回答
4

首先,我要赞扬您认识到正在发生的事情并明确说明这些状态(因为它们实际上是模型中的附加状态,而不是真正的动作转换)。我经常看到状态机最终像您的上一个示例一样(您想避免)。当您在事件处理程序中对“附加”状态变量进行测试时,这表明您的状态机具有更多您真正投入到设计中的状态——这些状态反映在设计中,而不是卡在现有状态的事件中带有一堆意大利面条编码的处理程序检查全局变量中编码的其他“状态”。

有几个 C++ 框架可以模拟分层状态机 - HSM - (这就是您的嵌套状态机想法听起来的样子),但我知道唯一支持直接 C 的框架是Quantum Framework,我认为购买这可能意味着相当程度的承诺(即,这可能不是一个简单的改变)。但是,如果您想研究这种可能性,Samek 写了很多关于如何在 C 中支持 HSM的文章(和一本书)。

但是,如果您不需要 HSM 模型的某些更复杂的部分(例如,“最内层”状态未处理的事件会冒泡以可能由父状态处理,则完全进入和退出支持整个状态层次结构),那么很容易支持嵌套状态机,就像完全独立的状态机在进入/退出父状态时碰巧启动和停止一样。

大状态机模型可能更容易实现(它只是现有框架中的几个状态)。我建议,如果将状态添加到您当前的状态机模式不会使模型过于复杂,那么就这样做吧。

换句话说,让最适合您的模型的东西驱动您如何在软件中实现状态机。

于 2009-09-10T21:11:22.590 回答
1

我认为没有单一的通用方法。正如其他人所说,这取决于您要做什么。

从广义上讲,我会避免将小型状态机嵌套在较大的状态机中,因为当您尝试简化事情时,您不仅会添加更多状态 - 从而增加复杂性,而且现在有两个状态变量需要跟踪。

特别是,当遍历“外部”状态机中的状态时,必须正确初始化“内部”状态变量。例如,如果由于错误,外部状态机中的转换无法重置内部状态机的状态变量怎么办?

一个可能的例外是所有内部状态机都做同样的事情。如果可以对数据进行参数化(例如,通过使用数组),那么您可以拥有内部状态机的单个实现,并且可以用计数器或类似物替换外部状态机。

举一个简单的例子:

#define MyDataSIZE 10

void UpdateStateMachine(void)
{
    static enum {BeginSTATE, DoStuffSTATE, EndSTATE} State = BeginSTATE;
    static unsigned int Counter = 0;
    static unsigned int MyData[MyDataSIZE];

    switch(State)
    {
        default:
        case BeginSTATE:
            /* Some code */
            if(/* Some condition*/)
                {State = DoStuffSTATE;}
            break;
        case DoStuffSTATE:
            /* Some actions on MyData[Counter] */
            if(/* Some condition*/)
                {State = EndSTATE;}
            break;
        case EndSTATE:
            /* Some code */
            if(/* Some condition*/)
            {
                Counter++;
                if(Counter >= MyDataSIZE)
                    {Counter = 0;}
                State = BeginSTATE;
            } /* if */
            break;
    } /* switch */
} /* UpdateStateMachine() */
于 2009-09-12T15:50:01.647 回答
1

正如您所提到的,大型状态机变得混乱,因此很难维护。几个较小的 SM 总是更容易理解和维护。

大 SM 的另一个缺点 - 更大的转换表,因此查找需要更长的时间。

于 2009-09-10T10:46:37.017 回答
0

为什么不使用状态模式

于 2009-09-10T11:02:14.637 回答
0

我投票支持更大的状态机,假设一台机器只能处于一个大状态机状态,它在逻辑上应该在那里。

通过使用一台大型机器,您可以使用环境的特性来防止同时存在两种状态的状态,从而使程序更安全,更易读。

还有一个大状态机的优点是任何其他程序员都可以通过查看一个地方(即,获得大图)轻松理解所有状态,而不是查看一个地方,希望了解细分,然后必须查看每个细分。

此外,正如您所建议的,使用多个状态机将迫使您发送更多参数,为每个状态执行多个测试,等等......

至于未来的期望,我是YAGNI的信徒。

于 2009-09-13T16:13:41.770 回答