Повысить параллельное поведение МСМ с задержкой самостоятельных переходов?

Я использую Boost MSM (базовые и функторные интерфейсы) и пытаюсь реализовать следующий конечный автомат:
введите описание изображения здесь

Прописью:

  1. Введите состояние State1
  2. Войдите в состояние A и выполните action_A. Через 2 секунды выведите «Trying again …» и повторно выполните состояние A (то есть вызовите его действие ввода). Это петли навсегда …
  3. В то же время, что и 2 (то есть «параллельно»), войдите в состояние B и выполните action_B. Через 5 секунд выведите «Trying again …» и повторно выполните состояние B (то есть вызовите его действие ввода). Это петли навсегда …

Я хотел бы знать, как создать этот конечный автомат в Boost MSM. Здесь есть две хитрости, которые я не могу понять, как это сделать:

  • Параллельное выполнение (т. Е. Запуск action_A не прекращает одновременное выполнение action_B)
  • Задержка перехода (то есть переходы, которые происходят через 2 секунды для состояния A и через 5 секунд для состояния B). Ни одна задержка не должна блокировать! Переходы должны просто «выстрелить» после этого времени.

Большое спасибо за помощь.

редактировать

Ответ @TakatoshiKondo делает то, что мне нужно, но я бы хотел получить более подробное объяснение некоторых частей ответа, чтобы полностью его понять.

  1. Как это соотносится с реализацией pthreads? Как вы думаете, Boost.Asio — лучшее решение, чем помещать состояния A и B в разные потоки и иметь блокирующие пассивные ожидания в каждом (например, то, что может быть достигнуто с помощью usleep(useconds_t usec) из unistd.h)? Мне кажется, что pthreads, которые я не пробовал использовать с Boost.MSM, будет более общей / менее ограниченной реализацией?
  2. Я не понимаю, как create а также process методы работы (почему create функции нужен шаблон с переменной частотой?). В частности, я раньше не работал с умными указателями или std::forward, поэтому, если бы вы могли дать человеческое объяснение каждой строки в этих функциях, было бы здорово (у меня мало времени, чтобы в общих чертах прочитать об этих функциях, чтобы попытаться понять этот код).
  3. В руки с 2, лучшее объяснение цели wp а также ios переменные-члены Sm было бы замечательно. Что вы имеете в виду, используя ios указатель на намеренно встретить копию конструктора? Я кроме того не вижу ios устанавливается где угодно, но не в конструкторе Sm(boost::asio::io_service* ios) : ios(ios) {}Что, кажется, ты никогда не звонишь?
  4. Внутри State1_ передний конец, у вас есть три BOOST_STATIC_ASSERT звонки в три on_entry методы. Что они делают?
  5. в main() функция, я смог удалить строку auto t = std::make_shared<boost::asio::deadline_timer>(ios); без изменения поведения — было ли это излишним?

0

Решение

Вот полный пример кода для этого:

// 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();
}

Давайте копать код.

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;
}

Он создает 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));
}

Код клиента вызывает функцию create () следующим образом:

    auto sm = Sm::create(&ios);

Sm имеет переменную-член ios для установки таймера крайнего срока. Интерфейс конечного автомата требуется для копирования MSM. Таким образом, ios является указателем io_service, а не ссылкой.

Состояния A и B являются ортогональными областями. Чтобы реализовать ортогональные области, определите несколько начальных состояний как mpl :: vector.

    typedef mpl::vector<A, B> initial_state;

Состояние A и B является составным состоянием. МСМ использует состояние подсхемы для реализации составных состояний. Наружный самый государственный 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.
}
};

Наконец я могу позвонить process() в действии государства А и Б.

        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());
}
);
}
};

редактировать

  1. Как это соотносится с реализацией pthreads? Как вы думаете, Boost.Asio — лучшее решение, чем помещать состояния A и B в разные потоки и иметь блокирующие пассивные ожидания в каждом (например, что может быть достигнуто с помощью usleep (useconds_t usec) из unistd.h)? Мне кажется, что pthreads, которые я не пробовал использовать с Boost.MSM, будет более общей / менее ограниченной реализацией?

Boost.MSM-х process_event() НЕ является потокобезопасным. Так что вам нужно заблокировать это. Увидеть Безопасность потоков в Boost msm
AFAIK, sleep () / usleep () / nanosleep () являются блокирующими функциями. Когда вы вызываете их в действии Boost.MSM, это означает, что они вызываются (ogirinally) из process_event(), И это требует блокировки. Наконец, блокировка wait блокирует друг друга (в данном случае after2 и after5). Поэтому я считаю, что асинхронный подход Boost.ASIO лучше.

  1. Мне не ясно, как работают методы создания и обработки (зачем функции создания нужен шаблон с переменным числом аргументов?). В частности, я ранее не работал со смарт-указателями или std :: forward, поэтому, если бы вы могли дать человеческое объяснение каждой строки в этих функциях, было бы здорово (у меня мало времени, чтобы прочитать об этих функциях в общих чертах в Для того, чтобы попытаться понять этот код).

Бэкэнд Boost.MSM наследует свой внешний интерфейс. Конструктор внешнего интерфейса Sm(boost::asio::io_service* ios):ios(ios) {}, В этом случае параметром конструктора является ios, Тем не менее, это может быть изменено в зависимости от варианта использования. Функция create() создает shared_ptr из back, А также backКонструктор перенаправляет все параметры во внешний интерфейс. Таким образом, аргумент IOS в auto sm = Sm::create(&ios); пересылается конструктору См. Причина, по которой я использую шаблоны variadic и 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;
}

К тому же, create() а также process() использовать параметры шаблона, которые с &&, Они называются пересылка-ссылка (универсальная ссылка). Это идиома называется совершенной пересылкой.
Увидеть http://en.cppreference.com/w/cpp/utility/forward

  1. В случае со 2 лучше было бы лучше объяснить назначение переменных-членов wp и ios для Sm. Что вы имеете в виду, используя указатель ios, чтобы преднамеренно встретить конструктор копирования? Более того, я не вижу, чтобы ios был установлен нигде, кроме как в конструкторе Sm (boost :: asio :: io_service * ios): ios (ios) {}, который, кажется, никогда не вызывается?

Boost.MSM пока не поддерживает переадресацию. Я написал запрос на просмотр. https://github.com/boostorg/msm/pull/8

Таким образом, forwarding-reference вызывает конструктор копирования в Boost.MSM. По этой причине я выбираю указатель boost :: asio :: io_service. Тем не менее, это не является существенным пунктом оригинального вопроса. Если я не использую forwarding-reference, я могу использовать ссылочные типы в 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;
}

std::ref не для make_shared. Это для Boost.MSM. Конструктор Boost.MSM требует указывать ссылку или нет из-за отсутствия поддержки пересылки ссылок.

  1. В интерфейсе State1_ у вас есть три вызова BOOST_STATIC_ASSERT в трех методах on_entry. Что они делают?

Это ничего не делает во время выполнения. Просто проверять тип Fsm во время компиляции. Иногда я путался с типом Fsm. Я думаю, что читатели также могут запутаться, поэтому я оставляю это в коде.

  1. В функции 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();
}
7

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]