关于erlang的有限状态机,参考erlang四大behaviour之二-gen_fsm这篇文章。
有限状态机可以用下面这个公式来表达
State(S) x Event(E) -> Actions(A), State(S')
这两天正好空闲,就用golang实现了一下,话不多说,直接上代码
package util import ( "errors" "reflect" "sync" "time" "unicode" "unicode/utf8" "util/log" ) var typeOfError = reflect.TypeOf((*error)(nil)).Elem() type FSM struct { sync.Mutex StopReason string rcvr reflect.Value // receiver of methods for the service typ reflect.Type // type of the receiver method map[string]reflect.Method event chan Event quit chan int state string stopped bool } type Event struct { event string param interface{} timeout int } func (fsm *FSM) IsStopped() bool { fsm.Lock() defer fsm.Unlock() return fsm.stopped } func (fsm *FSM) SendEvent(event string, param interface{}) { fsm.Lock() defer fsm.Unlock() if fsm.stopped { return } fsm.event <- Event{event, param, 0} } func (fsm *FSM) Init(start string) error { if _, ok := fsm.method[start]; !ok { return errors.New("not found state") } fsm.state = start go func() { for { select { case e := <-fsm.event: go fsm.CallState(e) case <-fsm.quit: goto close } } close: close(fsm.event) close(fsm.quit) }() return nil } func (fsm *FSM) CallState(e Event) { fsm.Lock() defer fsm.Unlock() if function, ok := fsm.method[fsm.state]; ok { returnValues := function.Func.Call([]reflect.Value{fsm.rcvr, reflect.ValueOf(e.event), reflect.ValueOf(e.param), reflect.ValueOf(e.timeout)}) nextstate := returnValues[0].String() timeout := returnValues[1].Int() errInter := returnValues[2].Interface() errmsg := "" if errInter != nil { errmsg = errInter.(error).Error() } if nextstate == "stop" { fsm.Stop(errmsg) fsm.quit <- 1 return } if errmsg != "" { log.LogError(errmsg) } fsm.state = nextstate if timeout > 0 { go fsm.DelayCall(time.Duration(timeout)) } } } func (fsm *FSM) DelayCall(timeout time.Duration) { select { case <-time.After(timeout * time.Millisecond): fsm.event <- Event{"timeout", 0, int(timeout)} } } func (fsm *FSM) Stop(message string) { fsm.StopReason = message fsm.stopped = true } func (fsm *FSM) Close() { fsm.Lock() defer fsm.Unlock() if fsm.stopped { return } fsm.quit <- 1 } func NewFSM(fsm interface{}) *FSM { f := &FSM{typ: reflect.TypeOf(fsm), rcvr: reflect.ValueOf(fsm), event: make(chan Event), quit: make(chan int)} f.method = suitableMethods(f.typ, true) return f } func isExported(name string) bool { rune, _ := utf8.DecodeRuneInString(name) return unicode.IsUpper(rune) } func suitableMethods(typ reflect.Type, reportErr bool) map[string]reflect.Method { methods := make(map[string]reflect.Method) for m := 0; m < typ.NumMethod(); m++ { method := typ.Method(m) mtype := method.Type mname := method.Name if !isExported(mname) { continue } // Method needs four ins: receiver, string, interface{}, int. if mtype.NumIn() != 4 { if reportErr { log.LogError("method", mname, "has wrong number of ins:", mtype.NumIn()) } continue } // First arg must be a string. if mtype.In(1).Kind() != reflect.String { if reportErr { log.LogError("method", mname, "arg1 type not a string:", mtype.In(1).Kind()) } continue } // Second arg must be a interface. if mtype.In(2).Kind() != reflect.Interface { if reportErr { log.LogError("method", mname, "arg2 type not a interface:", mtype.In(2).Kind()) } continue } // Third arg must be a int. if mtype.In(3).Kind() != reflect.Int { if reportErr { log.LogError("method", mname, "arg3 type not a int:", mtype.In(3).Kind()) } continue } // Method needs three out. if mtype.NumOut() != 3 { if reportErr { log.LogError("method", mname, "has wrong number of outs:", mtype.NumOut()) } continue } if mtype.Out(0).Kind() != reflect.String { if reportErr { log.LogError("method", mname, "out1 type not a string:", mtype.Out(0).Kind()) } continue } if mtype.Out(1).Kind() != reflect.Int { if reportErr { log.LogError("method", mname, "out1 type not a int:", mtype.Out(1).Kind()) } continue } if mtype.Out(2) != typeOfError { if reportErr { log.LogError("method", mname, "out3 type not a error:", mtype.In(2).Kind()) } continue } methods[mname] = method } return methods }
下面就是使用方法:
type GoFSM struct { } func (f *GoFSM) State1(event string, param interface{}, t int) (nextstate string, timeout int, err error) { log.LogMessage(event, param.(int)) return "State2", 100, nil //如果timeout大于0,则在timeout毫秒后,自动调用下一个状态,下一个状态的event为timeout } func (f *GoFSM) State2(event string, param interface{}, t int) (nextstate string, timeout int, err error) { log.LogMessage(event) return "stop", 0, errors.New("stop ok") //nextstate=stop则停止状态机,err为停止原因 } func main() { f := util.NewFSM(&GoFSM{}) f.Init("State1") //初始状态 f.SendEvent("Do", 1) //发送事件 time.Sleep(time.Second * 1) }
所有的状态回调函数,必须以大写字母开头,原型必须是
func(event string, param interface{}, t int) (nextstate string, timeout int, err error)event是事件名,param为事件的参数,t>0表示这是一个延时事件。返回值:nextstate为新的状态,必须和状态回调函数同名,如果为"stop"则表示没有后续的状态,状态机停止。timeout>0表示延时回调,将在timeout时间后,产生一个timeout事件。
有疑问加站长微信联系(非本文作者)