简单说就是作一件事可能会经过多个不同状态的转换, 转换依赖于在不同时间发生的不同事件来触发, 举个例子,比如 TCP的状态转换图, 在实现上就可以用FSM.
tcp.jpeg
传统的实现方案
FSM的实现方案
State
, 并确定此状态机的初始状态;Event
;Event
, 需要定义状态的转换过程Transition
;Event
匹配(注意: 相同的状态可能同时匹配多个Event);用张简图来说明一下
fsm.jpg
FSM的C++ 实现
StartBackground
, 开启一个work thread, 在这个work thread中不断从存储event的fifo队列中获取event后dispatch到各个machine;MachineSetHandler
, 将其实例注册到MachineSet, 从event的派发;一个具体的实际
enum class FoodEventType {
FET_LOGIN, // 开始登陆
FET_LOGIN_FAILED, // 登陆失败
FET_LOGIN_OK, // 登陆成功
FET_LOGOUT, // 登出
};
class FoodEvent : public kuafu::EventTemplate {
public:
using underlying_type = std::underlying_type::type;
FoodEvent(FoodEventType type, const kuafu::MachineBaseSharedPtr& machine)
:kuafu::EventTemplate(type, machine) {
}
...
}
};
class FoodMachine : public kuafu::StateMachine {
public:
FoodMachine(const std::string& name);
public:
virtual void Birth();
public:
// 需要用到的三种状态: 启动, 登陆中, 成功
kuafu::StateSharedPtr startup_;
kuafu::StateSharedPtr loging_;
kuafu::StateSharedPtr welcome_;
// 需要用到的几种转换
kuafu::TransitionSharedPtr startup_loging_;
kuafu::TransitionSharedPtr loging_welcome_;
kuafu::TransitionSharedPtr loging_startup_;
kuafu::TransitionSharedPtr welcome_startup_;
kuafu::TransitionSharedPtr welcome_timeout_;
};
Birth()
函数中构造 state和 transition, Birth()
是StateMachine
的一个虚函数, 每个用户实现的Machine都需要实现它:
void FoodMachine::Birth() {
startup_ = kuafu::State::MakeState(*this, "startup");
loging_ = kuafu::State::MakeState(*this, "loging");
welcome_ = kuafu::State::MakeState(*this, "welcom", 5000);
startup_loging_ = kuafu::Transition::MakeTransition("startup_loging",
startup_,
loging_,
std::make_shared>(FoodEventType::FET_LOGIN));
loging_welcome_ = kuafu::Transition::MakeTransition("loging_welcome",
loging_,
welcome_,
std::make_shared>(FoodEventType::FET_LOGIN_OK));
loging_startup_ = kuafu::Transition::MakeTransition("loging_startup",
loging_,
startup_,
std::make_shared>(FoodEventType::FET_LOGIN_FAILED));
welcome_startup_ = kuafu::Transition::MakeTransition("welcome_startup",
welcome_,
startup_,
std::make_shared>(FoodEventType::FET_LOGOUT));
welcome_timeout_ = kuafu::Transition::MakeTransition("welcome_timeout",
welcome_,
welcome_,
std::make_shared(type_));
}
MachineSet
, 并开始event处理线程
kuafu::MachineSetSharedPtr machine_set = kuafu::MachineSet::MakeMachineSet();
machine_set->StartBackground(500);
std::shared_ptr food_machine = kuafu::MakeMachine("food_machine");
food_machine->SetStartState(food_machine->startup_);
food_machine->startup_->OnEnter = [&](kuafu::MachineBase& machine,
const kuafu::StateSharedPtr& state) {
INFO_LOG("Enter " << state->GetName());
};
...
food_machine->startup_loging_->OnTransition = [&](kuafu::MachineBase& machine,
const kuafu::StateSharedPtr& from_state,
kuafu::ITransitionSharedPtr transition,
kuafu::EventSharedPtr event,
const kuafu::StateSharedPtr& to_state) {
INFO_LOG(transition->GetName()
<< " | "
<< from_state->GetName()
<< " -> "
<< to_state->GetName());
};
...
machine_set->Enqueue(std::make_shared(
kuafu::MachineOperator::MO_ADD,
food_machine));
machine_set->Enqueue(std::make_shared(
FoodEventType::FET_LOGIN,
food_machine));
machine_set->Enqueue(std::make_shared(
FoodEventType::FET_LOGIN_OK,
food_machine));
machine_set->Enqueue(std::make_shared(
FoodEventType::FET_LOGOUT,
food_machine));