【问题标题】:Boost Statechart vs. Meta State MachineBoost Statechart vs. 元状态机
【发布时间】:2011-05-15 14:32:58
【问题描述】:

显然 boost 包含两个独立的状态机库:StatechartMeta State Machine (MSM)。标语给出了非常相似的描述:

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

您知道两者的主要区别是什么以及在选择两者时有哪些注意事项?

【问题讨论】:

  • 呵呵,又一个很有趣但没人知道答案的案例……:)
  • :D 这个问题是我 SO 经验的巅峰之作!从两位开发人员那里得到答案......它可以变得更好吗?!非常感谢 Christophe 和 Andreas。
  • Statechart 让您将功能放入构造函数和析构函数中。这是一种反模式,尤其是对于析构函数。
  • 在状态图中,可以将退出操作放在一个单独的 exit() 处理程序中,该处理程序在销毁之前调用。我认为这项规定缓解了 Lev 提到的反模式的主要问题。
  • 作为第三个选择,你也可以考虑这个 C++11 FSM 库:github.com/skramm/spaghetti

标签: c++ boost state-machine boost-statechart boost-msm


【解决方案1】:

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

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

您可以通过查找在 MSM 审核期间发布的 cmets 来使自己获得更好的意见。这个话题在开发者名单上得到了很多讨论。

【讨论】:

  • 非常感谢。很高兴听到开发者自己的意见!现在我们只需要 Andreas Huber 的回应 :)
  • Minor nit-pick:在发布模式下,C++ RTTI (dynamic_cast, typeid) 的使用在 Boost.Statechart 中是严格可选的。
【解决方案2】:

正如 Christophe 已经提到的,这两个库之间的主要区别之一是运行时性能。虽然 MSM 可能会提供您所能得到的最好的,但 Statechart 有意识地用内存和处理器周期换取更好的可扩展性。

借助 Boost.Statechart,您可以将状态机的布局(即状态、转换)分布在多个翻译单元(cpp 文件)上,而 MSM 则无法做到这一点。与使用 MSM 相比,这使您可以使大型 FSM 的实现更易于维护并获得更快的编译速度。

当您问自己您的应用每秒必须处理多少事件时,通常很容易回答与 MSM 相比,Statechart 的性能开销是否真的对您的应用程序显着。

假设使用 Boost.Statechart 实现了一个中等复杂的 FSM,这里有一些大致数字:

  • 大多数当前的 PC 硬件可以轻松应对每秒超过 100,000 个事件
  • 即使是非常资源受限的硬件也将能够每秒处理数百个事件。

关于 CPU 负载,如果要处理的事件数量远低于这些数字,则与 MSM 相比,Boost.Statechart 开销几乎肯定不会引起注意。如果这个数字要高得多,那么使用 MSM 肯定会更好。

有关性能/可扩展性权衡的更深入信息,请参见此处: http://www.boost.org/doc/libs/1_45_0/libs/statechart/doc/performance.html

【讨论】:

【解决方案3】:

在编写我自己的 PPP 实现时,我使用 Statechart 有以下三个原因: 1)Statechart更简单,文档更清晰; 2) 我真的不喜欢 UML :)

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

【讨论】:

  • 虽然我同意 UML 的大部分内容是皇帝的新装,但状态图是 UML 中真正有价值的一件事。
  • 当然,但我从离散数学而不是软件工程中学习状态图。这留下了一个标记:)
【解决方案4】:

前段时间,我从 Statechart 开始并转向 MSM,因为它更容易与单线程中的 asio 结合使用。我没有设法将 Statechart 及其多线程功能与我对 asio 的使用结合起来——这可能是我对 Statechart 的某种新手不理解。我发现 MSM 更容易使用,因为它没有解决多线程问题。

【讨论】:

  • 大多数状态图类型也不涉及线程。关于多线程,您应该能够像 MSM 对应物一样使用 boost::statechart::state_machine。 boost::statechart::asynchronous_state_machine 和相关类型是状态图库的一个严格可选的部分。
【解决方案5】:

作为对 Tim 迟迟不参加讨论的回答(这也涉及 Lev 的早期 cmets 之一)。

作为那些在状态图中主张退出与析构函数分离的人之一(基于真实用例的论点,关于与现实世界的交互,即 I/O),当它被提交给 Boost 时,我同意可能存在问题将退出逻辑放入析构函数中。不出所料,David Abrahams 在异常安全方面也提出了有说服力的论点。由于这些原因,Statechart 不需要您将逻辑放入析构函数中 - 但它允许您使用通常的建议。

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

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

Statechart 将激活建模为对象的实例化,因此,如果您的构造函数有真正的工作/激活/实例化要做,并且如果它能够失败以致无法进入状态,Statechart 通过让您能够映射事件的例外。这是通过一种方式处理状态层次结构,寻找处理异常事件的外部状态,类似于基于调用堆栈的调用模型的堆栈展开方式。

这一切都有很好的记录 - 我建议您阅读文档并尝试一下。我建议你使用析构函数来清理“软件资源”和退出动作来执行“现实世界的退出动作”。

值得注意的是,异常传播在所有事件驱动的环境中都是一个问题,而不仅仅是状态图。最好在您的状态图设计中推理并包含错误/错误,并且当且仅当您无法以另一种方式处理它们时才诉诸异常映射。至少这对我有用 - ymmmv....

【讨论】:

  • 谢谢,我发现我的所有担忧都在 Boost::statechart 教程的“异常处理”部分得到了充分解决。在这种情况下,我认为 Lev 的(误导性)评论可以通过指向该教程的“两阶段退出”部分来解决。我会考虑删除我的答案,但您自己的答案会为该主题添加有价值的信息。
猜你喜欢
  • 1970-01-01
  • 2015-12-07
  • 1970-01-01
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多