Boost Statechart与元状态机


142

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

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

您知道两者之间的主要区别是什么,以及在两者之间进行选择时需要考虑的因素?


4
呵呵,另一种引起人们
极大

8
:D这个问题是我SO经验的顶峰!从两个开发人员那里获得答案...它还能变得更好吗?非常感谢Christophe和Andreas。
FireAphis 2010年

很好的问题,您设法获得了两个相互竞争的开发人员的答案!
Offirmo 2012年

3
Statechart使您可以将功能放入构造函数和析构函数中。这是一种反模式,尤其是对于析构函数而言。
2014年

2
在Statechart中,可以将退出操作放在单独的exit()处理程序中,该处理程序在销毁之前被调用。我认为这项规定减轻了列夫提到的反模式的主要问题。
蒂姆·克鲁斯

Answers:


116

似乎引起了很大的兴趣,请允许我发表我的(显然有偏见的)观点,因此应该带一点盐:

  • MSM更快
  • MSM不需要RTTI或任何虚拟的
  • MSM具有更完整的UML2支持(例如内部转换,符合UML的正交区域)
  • MSM提供一种描述性语言(实际上是几种)。例如,使用eUML前端,过渡可以描述为Source + Event [Guard] / Action == Target
  • MSM将使您的编译器遭受更大的状态机的困扰,因此您将需要一个新近使用的编译器(g ++> = 4.x,VC> = 9)

通过在MSM审阅期间查找张贴的评论,可以使自己成为更好的意见。开发人员列表中对该主题进行了很多讨论。


2
非常感谢你。听到开发人员本身的意见很高兴!现在我们只需要Andreas Huber的响应:)
FireAphis 2010年

16
次要选择:在发布模式下,Boost.Statechart严格选择使用C ++ RTTI(dynamic_cast,typeid)。

111

正如Christophe已经提到的,两个库之间的主要区别之一是运行时性能。尽管MSM可能会为您提供最好的解决方案,但Statechart会自觉地将内存和处理器周期换为更好的可伸缩性。

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

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

假设使用Boost.Statechart实现了一个中等复杂的FSM,以下是一些基本数字:

  • 当前大多数PC硬件将轻松处理每秒超过100000个事件
  • 即使资源非常有限的硬件,也能够每秒处理数百个事件。

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

有关性能/可伸缩性折衷的更多详细信息,请参见:http : //www.boost.org/doc/libs/1_45_0/libs/statechart/doc/performance.html


9
嗨,安德烈亚斯(Andreas),关于布局的传播,已经有了一些改进。您现在可以在不同的内核上编译子计算机。它不是完美的,但有明显的改进。见svn.boost.org/svn/boost/trunk/libs/msm/doc/HTML/...
克里斯托夫·亨利

11

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

Boost博士说,MSM至少快20倍,但对于大型FSM而言,编译速度很慢。


7
虽然我同意UML大部分是皇帝的新衣,但状态图是UML中真正有价值的一件事。
乔恩·特拉特温

4
当然,但是我从离散数学中学到了状态图,而不是软件工程学。这留下了一个记号:)
燃烧

4

前一段时间,我开始使用Statechart并移至MSM,因为它更易于从单个线程与asio结合使用。我没有通过使用asio来使Statechart及其多线程功能结合在一起-对我而言,这可能是对Statechart的某种新手理解。我发现MSM更容易使用,因为它没有解决多线程问题。


1
大多数状态图类型也不处理线程。关于多线程,您应该能够像MSM对应程序一样使用boost :: statechart :: state_machine。boost :: statechart :: asynchronous_state_machine和关联的类型是statechart库的严格可选部分。

2

为了回应Tim迟到的讨论(这也解决了Lev的早期评论之一)。

正如那些在状态图中主张将出口与析构函数分离的人(基于真实用例的论据,关于与现实世界(即I / O)的交互)早在提交给Boost时一样,我同意在退出时可能存在问题析构函数中的逻辑。大卫·亚伯拉罕(David Abrahams)毫不奇怪地就异常安全性也提出了有说服力的论据。出于这些原因,Statechart不需要您将逻辑放在析构函数中-但它允许您-使用通常的建议。

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

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

Statechart将激活建模为对象的实例化,因此,如果您的构造函数有实际工作/激活/实例化要做,并且能够失败而无法输入状态,则Statechart通过提供将异常映射到对象的能力来支持这一点。事件。这是通过处理状态层次结构以查找处理异常事件的外部状态的方式进行处理的,类似于为基于调用堆栈的调用模型取消堆栈的方式。

一切都有据可查-我建议您阅读文档并尝试一下。我建议您使用析构函数来清理“软件资源”并退出操作以执行“实际退出操作”。

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


谢谢,我看到在Boost :: statechart教程的“异常处理”部分中已充分解决了我的所有担忧。在那种情况下,我认为可以通过指向该教程的“两阶段退出”部分来解决Lev(具有误导性)的评论。我会考虑删除我的答案,除了您自己的答案会为该主题添加有价值的信息。
蒂姆·克鲁斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.