1. 程式人生 > 其它 >狀態機的實現探討

狀態機的實現探討

(譯)狀態機的實現探討

原文連結地址:http://drdobbs.com/cpp/184401236?pgno=1

         實現一個狀態機很容易,但是實現一個好的狀態機卻不簡單。一般實現狀態機的時候會有如下的實現程式碼:

switch (state_)

         case A:

                   do_A();

         case B:

                   do_B();

end switch

         當狀態量少並且各個狀態之間變化的邏輯比較簡單時,這種方法無可厚非,但是它有如下缺點:

l  邏輯程式碼較混亂;如狀態A到狀態B的切換,如果需要驗證有效性,那麼程式碼會變得臃腫,不再那麼直觀;示例:

case A:

         if (current_state != C)

                   return -1;

         else

                   current_state = A;

                   return 0;

case ....:

         ....

l  難擴充套件;大部分狀態的處理是相似的,而某些特殊的狀態則要特殊處理,比如需要提供附加資料,比如在Task中設定一個狀態為suspend,那麼需要傳遞一個要掛起的時間。這種情況類似於GUI程式中的事件通知介面,如:

handle_event(EventId event_, Long ext,...)

ext實際上可以傳遞任何東西。比如觸發了一個檔案拖動到圖示的事件dropOpen,那麼可以將要open的檔案路徑的地址通過ext傳入。這種方式挺萬金油的,所以在實現狀態機的時候,完全可以借鑑一下。

Context:

         假設場景如下:實現任務Task,它是一個狀態機,其狀態變化如圖:

l  Task被建立後假設獲取了必須資源,進入Ready狀態

l  Ready狀態可以被任務佇列執行run, 那麼Task進入Running狀態

l  Ready狀態時可以被suspend掛起,掛起時需要標識掛起的時間

l  Running狀態時可以被掛起

l  Suspended狀態可以通過潤使Task進入running狀態

l  Running、Ready、Suspended狀態都可以通過cancel,直接進入ended狀態

Question:

n  合理實現各個狀態之間的切換

n  方便擴充套件,任務狀態有可能會增加,任務的觸發時間可能會改變等,狀態機的實現必須能夠快速適應邏輯的變化

Solution:

         下面探討如下的實現方案:

u  設計基類:

  • 首先是用於傳遞擴充套件資料的萬金油虛類
#ifndef EVENT_DATA_H
#define EVENT_DATA_H
class EventData 
{
public:
    virtual ~EventData() {};  
    void*   data() = 0;
};
#endif //EVENT_DATA_H
  • 狀態的通用介面類StateMachine 介面, 此類不但定義了介面,其實其規定了狀態機實現的模板,任何狀態機的實現都可以按照此模板按部就班的實現.
#ifndef STATE_MACHINE_H
#define STATE_MACHINE_H
#include <stdio.h>
#include "EventData.h"
struct StateStruct;
// base class for state machines
class StateMachine 
{
public:
    StateMachine(int maxStates);
    virtual ~StateMachine() {}
protected:
    enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN };
    unsigned char currentState;
    void ExternalEvent(unsigned char, EventData* = NULL);
    void InternalEvent(unsigned char, EventData* = NULL);
    virtual const StateStruct* GetStateMap() = 0;
private:
    const int _maxStates;
    bool _eventGenerated;
    EventData* _pEventData;
    void StateEngine(void);
};
typedef void (StateMachine::*StateFunc)(EventData *);
struct StateStruct 
{
    StateFunc pStateFunc;    
};
#define BEGIN_STATE_MAP 
public:
const StateStruct* GetStateMap() {
    static const StateStruct StateMap[] = { 
#define STATE_MAP_ENTRY(entry)
    { reinterpret_cast<StateFunc>(entry) },
#define END_STATE_MAP 
    { reinterpret_cast<StateFunc>(NULL) }
    }; 
    return &StateMap[0]; }
#define BEGIN_TRANSITION_MAP 
    static const unsigned char TRANSITIONS[] = {
#define TRANSITION_MAP_ENTRY(entry)
    entry,
#define END_TRANSITION_MAP(data) 
    0 };
    ExternalEvent(TRANSITIONS[currentState], data);
#endif //STATE_MACHINE_H

ExternalEvent介面是帶有效性驗證的介面,他首先判斷狀態的有效性,如果有效則呼叫InternalEvent, InternalEvent是沒有驗證的內部介面,它直接的修改狀態。

  • StateMachine 的實現;此實現為通用的邏輯模板,任何狀態機的實現都可以套用此模板。
#include <assert.h>
#include "StateMachine.h"
StateMachine::StateMachine(int maxStates) :
    _maxStates(maxStates),
    currentState(0),
    _eventGenerated(false),
    _pEventData(NULL)
{
}    
// generates an external event. called once per external event 
// to start the state machine executing
void StateMachine::ExternalEvent(unsigned char newState, 
                                 EventData* pData)
{
    // if we are supposed to ignore this event
    if (newState == EVENT_IGNORED) {
        // just delete the event data, if any
        if (pData)  
            delete pData;
    }
    else if (newState == CANNOT_HAPPEN) {
        //! throw exception("xxx");
        //! or
        //! logerror("....");
    }
    else {
        // generate the event and execute the state engine
        InternalEvent(newState, pData); 
        StateEngine();                  
    }
}
// generates an internal event. called from within a state 
// function to transition to a new state
void StateMachine::InternalEvent(unsigned char newState, 
                                 EventData* pData)
{
    _pEventData = pData;
    _eventGenerated = true;
    currentState = newState;
}
// the state engine executes the state machine states
void StateMachine::StateEngine(void)
{
    EventData* pDataTemp = NULL;
    if (_eventGenerated) {         
        pDataTemp = _pEventData;  // copy of event data pointer
        _pEventData = NULL;       // event data used up, reset ptr
        _eventGenerated = false;  // event used up, reset flag
        assert(currentState < _maxStates);
        // execute the state passing in event data, if any
        const StateStruct* pStateMap = GetStateMap();
        (this->*pStateMap[currentState].pStateFunc)(pDataTemp);
        // if event data was used, then delete it
        if (pDataTemp) {
            delete pDataTemp;
            pDataTemp = NULL;
        }
    }
}

在這裡ExternalEvent判斷該狀態是否是有效的,如果是EVENT_IGNORED,那麼可以直接忽略此操作,如果是CANNOT_HAPPEN,說明出現了邏輯錯誤。

l  具體task的實現如下:

#ifndef TASK_H
#define TASK_H
#include "StateMachine.h"
struct TaskData : public EventData
{
    int xxx;
};
class Task : public StateMachine
{
public:
    Task() : StateMachine(ST_MAX_STATES) {}
    // external events taken by this state machine
    void Suspend();
    void Run();
    void Cancel();
private:
    // state machine state functions
    void ST_Ready();
    void ST_Running();
    void ST_Suspended(TaskData* pData);
    void ST_Ended();
    // state map to define state function order
    BEGIN_STATE_MAP
        STATE_MAP_ENTRY(ST_READY)
        STATE_MAP_ENTRY(ST_RUNNING)
        STATE_MAP_ENTRY(ST_SUSPENDED)
        STATE_MAP_ENTRY(ST_ENDED)
    END_STATE_MAP
    // state enumeration order must match the order of state
    // method entries in the state map
    enum E_States { 
        ST_READY = 0,
        ST_RUNNING,
        ST_SUSPENDED,
        ST_ENDED,
        ST_MAX_STATES
    };
};
#endif //MOTOR_H

BEGIN_STATE_MAP 巨集將自定義的狀態函式註冊到StateMap中,這樣可以直接通過state值索引得到其對應的狀態函式。

l  Task的實現程式碼

#include <assert.h>
#include "task.h"
void Task::Suspend(MotorData* pData)
{
    BEGIN_TRANSITION_MAP                      // - Current State -
        TRANSITION_MAP_ENTRY (ST_Suspended)   // ST_READY
        TRANSITION_MAP_ENTRY (ST_Suspended)   // ST_RUNNING
        TRANSITION_MAP_ENTRY (EVENT_IGNORED)  // ST_SUSPENDED
        TRANSITION_MAP_ENTRY (CANNOT_HAPPEN)  // ST_ENDED
    END_TRANSITION_MAP(pData)
}
void Task::Run(void)
{
    BEGIN_TRANSITION_MAP                      // - Current State -
        TRANSITION_MAP_ENTRY (ST_RUNNING)     // ST_READY
        TRANSITION_MAP_ENTRY (EVENT_IGNORED)  // ST_RUNNING
        TRANSITION_MAP_ENTRY (ST_RUNNING)     // ST_SUSPENDED
        TRANSITION_MAP_ENTRY (CANNOT_HAPPEN)  // ST_ENDED
    END_TRANSITION_MAP(NULL)
}
void Task::Cancel(void)
{
    BEGIN_TRANSITION_MAP                      // - Current State -
        TRANSITION_MAP_ENTRY (ST_ENDED)       // ST_READY
        TRANSITION_MAP_ENTRY (ST_ENDED)       // ST_RUNNING
        TRANSITION_MAP_ENTRY (ST_ENDED)       // ST_SUSPENDED
        TRANSITION_MAP_ENTRY (EVENT_IGNORED)        // ST_ENDED
    END_TRANSITION_MAP(NULL)
}
void Task::ST_Ready() 
{
    InternalEvent(ST_READY);
}
void Task::ST_Running()
{
    InternalEvent(ST_RUNNING);
}
void Task::ST_Suspended(MotorData* pData)
{
    InternalEvent(ST_SUSPENDED, pData);
}
void Task::ST_Ended()
{
    InternalEvent(ST_ENDED);
}

在狀態的處理上思路是:狀態要麼是有效的、要麼是可以忽略的、要麼是根本不會發生的。