Boost 状态图与元状态机

很明显,升级包含两个独立的状态机库: 状态图元状态机(MSM)。标语给出了非常相似的描述:

  • 状态图-任意复杂的有限状态机可以用易读和易维护的 C + + 代码实现。
  • 元状态机——一个用于表达 UML2有限状态机的高性能库。

你知道这两者之间的主要区别和选择时的考虑因素吗?

35512 次浏览

在编写我自己的购买力平价实现时,我使用状态图有三个原因: 1)状态图更简单,文件更清晰; 2)我真的不喜欢 UML:)

Boost 文档说 MSM 至少快20倍,但是对于大型 FSM 编译相当慢。

由于似乎有很多兴趣,请允许我给出我的(显然有偏见的)意见,因此应该持保留态度:

  • MSM 更快
  • MSM 不需要 RTTI 或任何虚拟的东西
  • MSM 有更完整的 UML2支持(例如内部转换,符合 UML 的正交区域)
  • MSM 提供了一种描述性语言(实际上有几种)。例如,使用 eUML 前端,转换可以描述为 Source + Event [ Guard ]/Action = = Target
  • MSM 会让你的编译器为更大的状态机而痛苦,所以你需要一个相当新的编译器(g + + > = 4.x,VC > = 9)

你可以通过在 MSM 的评论中寻找评论来使自己有一个更好的意见。这个主题在开发人员列表中讨论得很多。

正如 Christophe 已经提到的,两个库之间的关键区别之一是运行时性能。虽然 MSM 可能提供了您在这里能得到的最好的服务,但 Statechart 有意识地将内存和处理器周期转换为更好的可伸缩性。

和 Boost 一起。状态图可以将状态机的 布局(即状态、转换)分布在多个转换单元(cpp 文件)上,这是 MSM 所不能做到的。这使您能够使大型 FSM 的实现更具可维护性,并且比 MSM 编译速度快得多。

当你问自己你的应用程序每秒要处理多少事件时,Statechart 的性能开销是否会对你的应用程序产生重大影响通常是很容易回答的。

假设通过 Boost 实现了一个适度复杂的 FSM:

  • 目前大多数个人电脑硬件可以轻松处理每秒超过100,000个事件
  • 即使是 非常资源受限的硬件也能够每秒处理几百个事件。

关于 CPU 负载,如果要处理的事件数量远低于这些数字,Boost。与 MSM 相比,状态图开销几乎肯定不会引人注目。如果这个数字更高,你肯定是更好的男男性行为。

关于性能/可伸缩性权衡的更深入信息可以在这里找到: Http://www.boost.org/doc/libs/1_45_0/libs/statechart/doc/performance.html

一段时间以前,我开始使用 Statechart,后来转向 MSM,因为它更容易与单个线程的 asio 结合使用。我没有设法将 Statechart 及其多线程能力与我使用 asio 的方式结合起来——这可能是我对 Statechart 的某种新手不理解。我发现 MSM 更容易使用,因为它不处理多线程。

作为对 Tim 最后一次参与讨论的回应(这也回应了 Lev 早期的一个评论)。

作为那些在状态图中主张与析构函数分离退出的人之一(基于一个真实用例,关于与现实世界的交互,即 I/O) ,我同意在析构函数中加入退出逻辑是有问题的。不出所料,大卫 · 亚伯拉罕在异常安全方面也提出了有说服力的论点。由于这些原因,Statechart 并不要求您将逻辑放入析构函数中——但它允许您这样做——并提供通常的建议。

只能作为状态转换的一部分运行的逻辑(而不是整个状态图对象的破坏)可以(如果还需要进行资源清理的话,也应该这样做)被分离到一个单独的 exit ()操作中。

对于没有活动状态(资源)的“瘦”状态,只需要执行进入/退出操作,您可以在 ctor 和 d’tor 中执行这些操作,并确保构造函数和析构函数不会抛出。他们没有理由——没有执行 RAII 的状态——让这些地方的错误处理引发适当的事件并没有什么坏处。您可能仍然需要考虑是否需要退出操作来改变外部状态以在状态机销毁中运行... ... 如果您不希望在这种情况下发生退出操作,那么将它们放在退出操作中... ..。

状态图将激活建模为对象的实例化,所以如果你的构造函数有实际的工作/激活/实例化要做,并且如果它能够失败,以至于状态不能被输入状态,状态图通过给你映射异常到事件的能力来支持这一点。这种处理方式沿着状态层次结构向上,查找处理异常事件的外部状态,类似于基于调用堆栈的调用模型的堆栈展开方式。

这些都有很好的文档记录——我建议你读一下文档并试一试。我建议您使用析构函数来清理“软件资源”,并退出操作以执行“真实世界的退出操作”。

值得注意的是,异常传播在所有事件驱动的环境中都是一个小问题,而不仅仅是状态图。最好在状态图设计中推理和包含错误/错误,并且只有在无法以其他方式处理它们的情况下才使用异常映射。至少对我有用。