我熟悉MSM boost库。关于连接几个正交状态,我发现这非常有趣questions。
另外,我在boost文档中找到了一种在父文件中重用子状态机的方法,并且我编写了几个简单的嵌套状态机器。
然而,我无法理解如何将这两件事混合在一起。
我的目标是分叉到同一个子状态机的几个独立实例,并等到它们完成后再将控件返回到父控制器。
但是,boost documentation表示无法直接分叉到子状态机。
注意(也适用于分叉):目前,无法使用子机作为显式条目的目标。请使用输入伪状态获得几乎相同的效果。
示例
购买票务飞机可以作为子状态机的模型,您请求价格,然后执行一些评估,而不是您可以购买它(取消并且也不可能完成订单)。但同样的逻辑适用于几家航空公司。 现在,让我们假设我们想要创建一个扫描市场并从不同提供商预订一定数量的票据的软件。 当我们收到客户的订单时,我们会查询市场,我们决定从4家不同的航空公司购买(客户并不介意)。 现在,4子状态机将并行工作,只有在最后一个订单完成时,控制才会返回到父状态机。 (我知道这个例子并不好,但我希望你能得到这个想法)。
问题
是否可以分叉并使用同一状态机的多个实例(或实现与伪状态几乎相同的效果)?状态机如何知道事件指的是哪个子状态机?
如果有可能,请指点我正确的方向?我知道这个提升文档example,但它并没有真正解决我的问题。
我现在处于项目的早期阶段,因此如果需要,我仍然可以移动到不同的状态机库(提升状态图也在评估中)。
答案 0 :(得分:1)
我认为正交状态在状态的不同部分运行良好但处理相同的事件。在您的情况下,您需要多个状态机实例。为了解决你的情况,我认为定义两个不同的状态机是更好的方法。他们没有父子关系,但互相发送事件。
假设父状态机为管理器,子状态机为工作者。这是两个独立的状态机:
worker
+-----------+
+--|processing |
| | |
| +-----------+
| |
| | e_found(price) / a_notify(price) (send e_found(price) to the manager
| V
| +-----------+
| |processed |
| +-----------+
|e_cancel|
| V
| +-----------+
+->|end |
+-----------+
manager
+-------------------------------------------------------+
|comparing |
|entry/create 4 workers |
|e_found(price) / increment count and compare price, etc|
+-------------------------------------------------------+
| e_cancel/ propagate e_cancel to all workers
V
+-----------+
|end |
+-----------+
我相信状态机会说明你的示例场景。以下是代码: http://melpon.org/wandbox/permlink/JLkDaZBpvnAYLn5E
注意:上面的在线编译器输出有时不稳定,但我检查了本地环境中的代码并且工作正常。
我在main()中发送了一个发现票价的事件。这不是真的。也许从系统的其他部分接收价格数据。但这不是重点。
为了在状态机之间发送事件,我使用了msm :: back :: state_machine的shared_ptr。我需要让状态机的后端调用process_event,但模板参数Fsm是前端。所以我将状态机后端的weak_ptr作为成员变量嵌入到状态机的前端。当我想调用process_event()时,使用get_sm()成员函数获取后端的shared_ptr。
在这种情况下,取消事件从经理发送到工作人员。并且发现事件是从工人发送到经理。
如果要将worker作为不同的线程运行,则需要按如下方式使用互斥锁:
http://melpon.org/wandbox/permlink/TrexYnffiwEpazNk
见第100和192行。
取消事件由经理处理并传播给所有工作人员。要测试取消操作,可以按如下方式插入cancel事件:
int main() {
auto m = manager::create();
m->workers_[0]->process_event(e_found {10});
m->workers_[1]->process_event(e_found {20});
m->process_event(e_cancel()); // Cancel !!
m->workers_[2]->process_event(e_found {30});
m->workers_[3]->process_event(e_found {40});
return 0;
}