spa*_*ger 1 c++ parallel-processing boost wait boost-msm
我正在使用Boost MSM(基本和仿函数前端),我正在尝试实现以下状态机:

用语言:
我想知道在Boost MSM中创建这个状态机的方法.这里有两个技巧,我无法弄清楚如何做:
非常感谢您的帮助.
编辑
@TakatoshiKondo回答做了我需要的,但我想对答案的某些部分有更多的解释,以便完全理解它.
usleep(useconds_t usec)的unistd.h)?我的感觉是,我没有尝试过使用Boost.MSM的pthreads会是一个更通用/更少约束的实现吗?create和process方法是如何工作的(为什么create函数需要一个可变参数模板?).特别是,我之前没有使用过智能指针std::forward,所以如果你能对这些函数中的每一行给出一个人工解释,那就太棒了(我很难及时阅读这些特性,以便尝试理解这段代码).wp和ios成员变量的目的Sm将是伟大的.使用ios指针故意满足复制构造函数是什么意思?我还没有看到ios被设置在任何地方,但在构造函数中Sm(boost::asio::io_service* ios) : ios(ios) {},似乎你从来没有打电话?State1_前端内部,您可以BOOST_STATIC_ASSERT在三种on_entry方法中进行三次调用.这些是做什么的?main()函数中,我能够删除行auto t = std::make_shared<boost::asio::deadline_timer>(ios);而不改变行为 - 它是多余的吗?Tak*_*ndo 10
这是一个完整的代码示例:
// g++ example.cpp -lboost_system
#include <iostream>
#include <boost/asio.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- State machine
struct Sm : msmf::state_machine_def<Sm> {
using back = msm::back::state_machine<Sm>;
template <typename... T>
static std::shared_ptr<back> create(T&&... t) {
auto p = std::make_shared<back>(std::forward<T>(t)...);
p->wp = p; // set wp after creation.
return p;
}
template <typename Ev>
void process(Ev&& ev) {
// process_event via backend weak_ptr
wp.lock()->process_event(std::forward<Ev>(ev));
}
// ----- Events
struct EvSetParent {};
struct After2 {};
struct After5 {};
Sm(boost::asio::io_service* ios):ios(ios) {}
struct State1_:msmf::state_machine_def<State1_> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, Sm>::value));
std::cout << "State1::on_entry()" << std::endl;
f.process(EvSetParent());
}
struct Action {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm&, SourceState&, TargetState&) const {
std::cout << "Trying again..." << std::endl;
}
};
struct A:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "A::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(*f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(2));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After2());
}
);
}
};
struct B:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "B::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(*f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(5));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After5());
}
);
}
};
// Set initial state
typedef mpl::vector<A, B> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < A, After2, A, Action, msmf::none >,
msmf::Row < B, After5, B, Action, msmf::none >
> {};
Sm* parent;
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State1 initial_state;
struct ActSetParent {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm& f, SourceState& s, TargetState&) const {
std::cout << "ActSetIos" << std::endl;
s.parent = &f; // set parent state machine to use process() in A and B.
}
};
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, EvSetParent, msmf::none, ActSetParent, msmf::none >
> {};
// front-end can access to back-end via wp.
std::weak_ptr<back> wp;
boost::asio::io_service* ios; // use pointer intentionally to meet copy constructible
};
int main() {
boost::asio::io_service ios;
auto t = std::make_shared<boost::asio::deadline_timer>(ios);
auto sm = Sm::create(&ios);
ios.post(
[&]{
sm->start();
}
);
ios.run();
}
Run Code Online (Sandbox Code Playgroud)
我们来挖掘代码.
Boost.MSM不支持延迟事件触发机制.所以我们需要一些定时器处理机制.我选择Boost.Asio截止时间计时器.它适用于事件驱动的库,如Boost.MSM.
为了在状态机的前端调用process_event(),它需要知道它的后端.所以我写了create()函数.
template <typename... T>
static std::shared_ptr<back> create(T&&... t) {
auto p = std::make_shared<back>(std::forward<T>(t)...);
p->wp = p; // set wp after creation.
return p;
}
Run Code Online (Sandbox Code Playgroud)
它创建后端的shared_ptr然后,并将其分配给weak_ptr.如果weak_ptr设置正确,那么我可以调用process_event()如下.我写了一个包装器process().
template <typename Ev>
void process(Ev&& ev) {
// process_event via backend weak_ptr
wp.lock()->process_event(std::forward<Ev>(ev));
}
Run Code Online (Sandbox Code Playgroud)
客户端代码调用create()函数,如下所示:
auto sm = Sm::create(&ios);
Run Code Online (Sandbox Code Playgroud)
Sm具有成员变量ios来设置截止时间计时器.状态机的前端需要由MSM复制.所以ios是io_service的指针而不是引用.
状态A和B是正交区域.为了实现正交区域,将多个初始状态定义为mpl :: vector.
typedef mpl::vector<A, B> initial_state;
Run Code Online (Sandbox Code Playgroud)
状态A和B是复合状态.MSM使用子机状态来实现复合状态.外部大多数状态Sm是状态机,State1_也是状态机.我在状态A和B的输入操作中设置了一个计时器.当计时器被触发时,调用process().但是,processs()是一个成员函数Sm,而不是State1_.所以,我需要实现一些机制来访问Sm的Stete1_.我加入成员变量parent到State1_.这是一个指针Sm.在进入的动作中State1_,我调用process()并且事件是PEvSetParent . It simply invokesActSetParent . In the action, SourceState isState1_`.我将父成员变量设置为父指针,如下所示:
struct ActSetParent {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm& f, SourceState& s, TargetState&) const {
std::cout << "ActSetIos" << std::endl;
s.parent = &f; // set parent state machine to use process() in A and B.
}
};
Run Code Online (Sandbox Code Playgroud)
最后,我可以打电话给process()A和B州的行动.
struct A:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "A::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(*f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(2));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After2());
}
);
}
};
Run Code Online (Sandbox Code Playgroud)
编辑
- 这与pthreads实现相比如何?你认为Boost.Asio是一个更好的解决方案,而不是将状态A和B放入不同的线程并且在每个线程中都有阻塞,被动等待(例如通过unistd.h的usleep(useconds_t usec)可以实现的)?我的感觉是,我没有尝试过使用Boost.MSM的pthreads会是一个更通用/更少约束的实现吗?
Boost.MSM process_event()不是线程安全的.所以你需要锁定它.请参阅Boost msm
AFAIK中的线程安全性,sleep()/ usleep()/ nanosleep()是阻塞函数.当你在Boost.MSM的行动中调用它们时,这意味着它们被称为(ogirinally)process_event().它需要锁定.最后,阻塞等待块(在这种情况下,after2和after5).因此我认为Boost.ASIO的异步approch更好.
- 我不清楚创建和处理方法是如何工作的(为什么create function需要一个可变参数模板?).特别是,我以前没有使用过智能指针或std :: forward,所以如果你能对这些函数中的每一行给出一个人工解释,那就太棒了(我很难及时阅读这些特性)为了试图理解这段代码).
Boost.MSM的后端继承了它的前端.前端构造函数是Sm(boost::asio::io_service* ios):ios(ios) {}.在这种情况下,构造函数的参数是ios.但是,它可以根据用例改变.该函数create()创建一个shared_ptr back.并且back构造函数将所有参数转发到前端.因此,ios的参数auto sm = Sm::create(&ios);被转发给Sm的构造函数.我使用可变参数模板和std :: forward的原因是最大化灵活性.如果改变了Sm的构造函数的参数,我不需要改变create()函数.您可以create()按如下方式更改功能:
static std::shared_ptr<back> create(boost::asio::io_service* ios) {
auto p = std::make_shared<back>(ios);
p->wp = p; // set wp after creation.
return p;
}
Run Code Online (Sandbox Code Playgroud)
另外,create()并process()使用模板参数&&.它们被称为转发参考(通用参考).这是一种称为完美转发的习语.见http://en.cppreference.com/w/cpp/utility/forward
- 与2一起,更好地解释Sm的wp和ios成员变量的目的将是很好的.使用ios指针故意满足复制构造函数是什么意思?我还没有看到ios被设置在任何地方,但在构造函数Sm(boost :: asio :: io_service*ios):ios(ios){},它似乎你从不打电话?
到目前为止,Boost.MSM不支持转发引用.我写了一个拉请求请参阅https://github.com/boostorg/msm/pull/8
因此,forwarding-reference在Boost.MSM中调用copy-constructor.这就是我选择boost :: asio :: io_service指针的原因.但是,这不是原始问题的重点.如果我不使用转发引用,我可以使用引用类型Sm.所以我更新代码如下:
static std::shared_ptr<back> create(boost::asio::io_service& ios) {
auto p = std::make_shared<back>(std::ref(ios));
p->wp = p; // set wp after creation.
return p;
}
Run Code Online (Sandbox Code Playgroud)
std::ref不适合make_shared.它适用于Boost.MSM.由于缺少转发引用支持,Boost.MSM的构造函数需要指定引用.
- 在State1_前端内,您在三个on_entry方法中有三个BOOST_STATIC_ASSERT调用.这些是做什么的?
它在运行时没有任何作用.只是在编译时检查Fsm的类型.有时候我对Fsm的类型感到困惑.我猜读者也可能会感到困惑,所以我把它留在代码中.
- 在main()函数中,我能够删除auto t = std :: make_shared(ios); 不改变行为 - 这是多余的吗?
啊哈,我忘了擦掉它.我更新了代码.
这是更新的代码:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- State machine
struct Sm : msmf::state_machine_def<Sm> {
using back = msm::back::state_machine<Sm>;
static std::shared_ptr<back> create(boost::asio::io_service& ios) {
auto p = std::make_shared<back>(std::ref(ios));
p->wp = p; // set wp after creation.
return p;
}
template <typename Ev>
void process(Ev&& ev) {
// process_event via backend weak_ptr
wp.lock()->process_event(std::forward<Ev>(ev));
}
// ----- Events
struct EvSetParent {};
struct After2 {};
struct After5 {};
Sm(boost::asio::io_service& ios):ios(ios) {}
struct State1_:msmf::state_machine_def<State1_> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, Sm>::value));
std::cout << "State1::on_entry()" << std::endl;
f.process(EvSetParent());
}
struct Action {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm&, SourceState&, TargetState&) const {
std::cout << "Trying again..." << std::endl;
}
};
struct A:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "A::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(2));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After2());
}
);
}
};
struct B:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "B::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(5));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After5());
}
);
}
};
// Set initial state
typedef mpl::vector<A, B> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < A, After2, A, Action, msmf::none >,
msmf::Row < B, After5, B, Action, msmf::none >
> {};
Sm* parent;
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State1 initial_state;
struct ActSetParent {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm& f, SourceState& s, TargetState&) const {
std::cout << "ActSetIos" << std::endl;
s.parent = &f; // set parent state machine to use process() in A and B.
}
};
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, EvSetParent, msmf::none, ActSetParent, msmf::none >
> {};
// front-end can access to back-end via wp.
std::weak_ptr<back> wp;
boost::asio::io_service& ios;
};
int main() {
boost::asio::io_service ios;
auto sm = Sm::create(ios);
ios.post(
[&]{
sm->start();
}
);
ios.run();
}
Run Code Online (Sandbox Code Playgroud)