大状态机与嵌套状态机
我在实时系统中有一个状态机,只有很少 (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;
}
}
解决此类问题的一般最佳方法是什么:许多小型嵌套状态机(具有许多无效组合)、一个大型状态机还是其他任何东西?
I have a statemachine in a real-time system with very few (3) states.
typedef enum {
STATE1,
STATE2,
STATE3
} state_t;
However, the transitions between those states need considerable time and have their own subdivisions. So I have two choices, either I extend the main statemachine such that all the intermediate states are represented:
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;
or I create a nested statemachine for the relevant main states:
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;
...
Both possibilities have their advantages and drawbacks. The big statemachine gets messy and complicated very easily. However, having all the states consistent in the second case isn't trivial either and many functions would need information about both the global state and the substates.
I'd like to avoid complicated code which has to handle several parallel states, like:
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;
}
}
What is the general best approach for such a problem: many small and nested statemachines (with many invalid combinations), one big statemachine or anything else?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
许多小型状态机将为您提供更多的代码灵活性,特别是当您需要重新设计任何东西时。那么您应该(希望)能够更改嵌套状态机,而不必更改任何其他嵌套状态机。
拥有更大的转换表不应该导致更长的查找,因为我假设您在内存中合理地布置了表。如果有的话,您实际上应该能够从大型机器中获得更多的速度,仅仅是因为您没有使用小型状态机在它们之间干净地转换可能需要的额外的一两个步骤。但考虑到这种方法增加了复杂性,我建议如下:使用嵌套状态机进行设计,然后一旦一切正常,如果需要的话重构为单个状态机以获得一点速度提升。
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.
首先,我想赞扬您认识到正在发生的事情并使这些状态变得明确(因为它们实际上是模型中的附加状态,而不是真正通过操作进行转换)。我经常看到状态机最终像你最后一个例子一样(你想避免)。当您在事件处理程序中测试“附加”状态变量时,这表明您的状态机具有更多已真正放入设计中的状态 - 这些显示会反映在设计中,而不是塞入现有状态的事件中带有一堆意大利面条编码的处理程序检查全局变量中编码的附加“状态”。
有几个 C++ 框架可以模拟分层状态机 - HSM -(这就是您的嵌套状态机想法听起来的样子),但我知道唯一支持直接 C 的框架是 量子框架,我认为购买它可能意味着相当程度的承诺(即,它可能不是一个简单的改变)。但是,如果您想研究这种可能性,Samek 已经写了很多文章(和一本关于如何在 C 中支持 HSM 的书。
但是,如果您不需要 HSM 模型的一些更复杂的部分(例如不由“最内部”状态处理的事件会冒泡)最多可能由父状态处理,对整个状态层次结构的完全进入和退出支持),那么很容易支持嵌套状态机,就像在进入/退出父状态时恰好启动和停止的完全独立的状态机一样。
大状态机模型可能更容易实现一些(它只是现有框架中的几个状态)。我建议,如果将状态添加到当前状态机模式不会使模型变得太复杂,那么就这样做。
换句话说,让最适合您的模型的方式驱动您在软件中实现状态机的方式。
First, I want to commend you for recognizing what's happening and making these states explicit (since they are in fact additional states in your model, not really transitions with an action). Far too often I see state machines that end up like your last example (that you want to avoid). When you have tests for 'additional' state variables inside your event handlers, it's a sign that your state machine has more states that you've really put into the design - those show be reflected in the design, not jammed into the existing state's event handlers with a bunch of spaghetti coded checks for additional 'state' encoded in global variables.
There are several frameworks for C++ that model hierarchical state machines - HSMs - (which is what your nested state machine idea sounds like), but the only one I'm aware of that supports straight C is Quantum Framework, and I think that buying into that would probably mean a decent level of commitment (i.e., it's probably not a simple change). However, if you want to look into this possibility, Samek has written a lot of articles (and a book) about how to support HSMs in C.
However, if you don't need some of the more sophisticated parts of the HSM models (such as events that aren't handled by the 'innermost' state get bubbled up to be possibly handled by parent states, full enter and exit support for the entire state hierarchy), then it's pretty easy to support nested state machines just as completely independent state machines that happen to start and stop when a parent state is entered/exited.
The big state machine model is probably a bit easier to implement (it's just several more states in your existing framework). I'd suggest that if adding the states to your current state machine mode doesn't make the model too complex, then just go with that.
In other words, let what works best for your model drive how you implement the state machine in software.
正如您提到的,大型状态机变得混乱,因此很难维护。几个较小的 SM 总是更容易理解和维护。
大 SM 的另一个缺点 - 更大的转换表,因此查找时间更长。
As you mentioned, big state machine gets messy so it is very difficult to maintain. Several smaller SMs are always easier to understand and maintain.
Another drawback of big SM - bigger transition table, thus lookup takes longer.
我认为不存在单一的、通用的方法。正如其他人所说,这取决于您想要做什么。
从广义上讲,我会避免将小型状态机嵌套在较大的状态机中,因为您不仅会添加更多状态 - 从而增加复杂性 - 当您试图简化事物时,您现在还有两个状态变量需要跟踪。
特别是,当遍历“外部”状态机中的状态时,必须正确初始化“内部”状态变量。例如,如果由于错误而导致外部状态机发生转换而无法重置内部状态机的状态变量,该怎么办?
一个可能的例外是所有内部状态机都执行相同的操作。如果可以参数化数据(例如,通过使用数组),那么您可以拥有内部状态机的单个实现,并且可以用计数器或类似的东西替换外部状态机。
举一个简单的例子:
I don't think there is a single, general approach. As others have said, it depends on what you're trying to do.
Broadly speaking, I'd avoid nesting small state machines inside larger ones, as not only are you adding more states - and hence complexity - when you're trying to simplify things, you now have two state variables to keep track of.
In particular, the "inner" state variable has to be properly initialised when traversing states in the "outer" state machine. For example, what if, due to a bug, there's a transition in the outer state machine which fails to reset the state variable for the inner state machines?
The one possible exception to this is where all the inner state machines do the same thing. If it's possible to parameterise the data (say, by using an array), then you can have a single implementation of the inner state machine, and it may be possible to replace the outer state machine with a counter or similar.
To give a simplistic example:
为什么不使用状态模式?
Why don't you use the state pattern?
我投票支持更大的状态机,假设单个机器只能处于大状态机状态之一,从逻辑上讲它应该在那里。
通过使用一台大机器,您可以利用环境的特性来防止同时存在两种状态,从而使程序更安全、更具可读性。
此外,一个大状态机的优点是任何其他程序员都可以通过查看单个位置(即了解大局)轻松理解所有状态,而不是查看单个位置,希望了解细分,然后必须查看每个细分部分。
另外,正如您所建议的,使用多个状态机将迫使您发送更多参数,为每个状态执行多个测试,等等'...
至于未来的期望,我相信 YAGNI。
I vote for the bigger state machine, assuming that a single machine can only be in one of the big state machine states, it should logically be there.
By using one big machine you are using a characteristic of the environment to prevent a state in which two states exist at the same time, making the program safer, and more readable.
Also one big state machine has the advantage that any other programmer can easily understand all the states by looking in a single place (that is, get the big picture), vs looking in a single place, hopefully be aware of the sub-division, then having to look at each sub-division.
Also as you've suggested working with several state machines will force you to send more parameters, performing more than one test for each state, etc'...
As for future expectations I'm a believer in the YAGNI.