前前言
这个类经过我的正式投入使用啊,发现不对劲,这样做可能会导致线程死锁
比如你dispatch一个event,然后在这个回调里把那个事件的侦听给remove掉了,那么就会导致线程死锁(这个问题找了好久啊,刚刚调试的时候才发现了)
还有就是获取func的引用的问题,golang那半c半java的语法,我改用了新的方法
源码已经修改!
前言:
呀,学Go语言两周了,感觉上手挺快的,golang虽然和c语言很想,但是避免掉了很多指针相关的东东,所以学起来特别轻松。
但是途中坎坷颇多啊,资料是少之又少啊,搜索引擎都搜不到啥东西,唯有和上才有比较完整的资料,加了个golang群么,大牛都潜水不说话,然后我瞎了,只能自己琢磨。
也是自己闹着玩吧,最近想写一个服务器(目前已经能同步移动了),我也想学一门后端语言,于是就选了google的go语言了,听说并发性能挺好的。
然而,golang这语言貌似都是被用作web服务开发了的,群里一般都是在讨论web开发的问题,而我是做socket开发的,当然,golang里面不叫socket。
工作比较忙,只有下班后那点时间来学golang了,学得比较基础,大牛就当走走场好了。
正题:
我是一名页游前端开发人员,当然我是as3开发者,对as3的观察者模式-事件机制,那是太依赖了,而golang里面原生并不提供这种机制。
golang有的只是十分相似的goroutine,也就是底层支持的并发机制,然后线程间通讯就是channel,好比于as3中的Event,当然不能直接比较,要封装过。
观察者模式就是指一对多的依赖关系,生产者分派消息,消费者全都能收到消息(全局观察模式),这样,可以降低模块间的耦合度,我们要做的,就是来管理这三者。
然后,用golang来实现这一设计模式是很简单的,我仅用了一百多行,就简单地实现了,直接看代码吧:
1 package tbs 2 3 import ( 4 //"fmt" 5 "unsafe" 6 ) 7 8 type Dispatcher struct { 9 listeners map[string]*EventChain 10 } 11 12 type EventChain struct { 13 chs []chan *Event 14 callbacks []*EventCallback 15 } 16 17 func createEventChain() *EventChain { 18 return &EventChain{chs: []chan *Event{}, callbacks: []*EventCallback{}} 19 } 20 21 type Event struct { 22 eventName string 23 Params map[string]interface{} 24 } 25 26 func CreateEvent(eventName string, params map[string]interface{}) *Event { 27 return &Event{eventName: eventName, Params: params} 28 } 29 30 type EventCallback func(*Event) 31 32 var _instance *Dispatcher 33 34 func SharedDispatcher() *Dispatcher { 35 if _instance == nil { 36 _instance = &Dispatcher{} 37 _instance.Init() 38 } 39 40 return _instance 41 } 42 43 func (this *Dispatcher) Init() { 44 this.listeners = make(map[string]*EventChain) 45 } 46 47 func (this *Dispatcher) AddEventListener(eventName string, callback *EventCallback) { 48 eventChain, ok := this.listeners[eventName] 49 if !ok { 50 eventChain = createEventChain() 51 this.listeners[eventName] = eventChain 52 } 53 54 exist := false 55 //fmt.Println("add len:", len(eventChain.callbacks)) 56 for _, item := range eventChain.callbacks { 57 a := *(*int)(unsafe.Pointer(item)) 58 b := *(*int)(unsafe.Pointer(callback)) 59 //fmt.Println("add", a, b) 60 if a == b { 61 exist = true 62 break 63 } 64 } 65 66 if exist { 67 return 68 } 69 70 ch := make(chan *Event) 71 72 eventChain.chs = append(eventChain.chs[:], ch) 73 eventChain.callbacks = append(eventChain.callbacks[:], callback) 74 75 go this.handler(eventName, ch, callback) 76 } 77 78 func (this *Dispatcher) handler(eventName string, ch chan *Event, callback *EventCallback) { 79 //fmt.Printf("add listener: %s\n", eventName) 80 //fmt.Println("chan: ", ch) 81 for { 82 event := <-ch 83 //fmt.Println("event out:", eventName, event, ch) 84 if event == nil { 85 break 86 } 87 go (*callback)(event) 88 } 89 } 90 91 func (this *Dispatcher) RemoveEventListener(eventName string, callback *EventCallback) { 92 eventChain, ok := this.listeners[eventName] 93 if !ok { 94 return 95 } 96 97 var ch chan *Event 98 exist := false 99 key := 0100 for k, item := range eventChain.callbacks {101 a := *(*int)(unsafe.Pointer(item))102 b := *(*int)(unsafe.Pointer(callback))103 //fmt.Println("remove", a, b)104 if a == b {105 exist = true106 ch = eventChain.chs[k]107 key = k108 break109 }110 }111 112 if exist {113 //fmt.Printf("remove listener: %s\n", eventName)114 //fmt.Println("chan: ", ch)115 ch <- nil116 117 eventChain.chs = append(eventChain.chs[:key], eventChain.chs[key+1:]...)118 eventChain.callbacks = append(eventChain.callbacks[:key], eventChain.callbacks[key+1:]...)119 //fmt.Println(len(eventChain.chs))120 }121 }122 123 func (this *Dispatcher) DispatchEvent(event *Event) {124 eventChain, ok := this.listeners[event.eventName]125 if ok {126 ////fmt.Printf("dispatch event: %s\n", event.eventName)127 for _, chEvent := range eventChain.chs {128 chEvent <- event129 }130 }131 }
这个类里定义了三个结构,Dispatcher:分派器主类,Event:事件类,EventChain:事件链类
如果你要使用这个类,那你只要那Dispatcher的单例方法:
SharedDispatcher()
来进行操作好了
要创建Event,你是要使用创建方法
CreateEvent(eventNamestring,paramsmap[string]interface{})
来创建
当然,demo还得贴上
1 package main 2 3 import ( 4 "fmt" 5 "tbs" 6 "time" 7 ) 8 9 type MClass struct {10 dispatcher tbs.Dispatcher11 }12 13 func main() {14 mc := &MClass{}15 mc.Start()16 }17 18 func (this *MClass) Start() {19 //获取分派器单例20 dispatcher := tbs.SharedDispatcher()21 22 //添加监听123 var fun1 tbs.EventCallback = this.onTest24 dispatcher.AddEventListener("test", &fun1)25 26 //再添加监听227 var fun2 tbs.EventCallback = this.onTest228 dispatcher.AddEventListener("test", &fun2)29 30 //随便弄个事件携带的参数,我把参数定义为一个map31 params := make(map[string]interface{})32 params["id"] = 100033 //创建一个事件对象34 event := tbs.CreateEvent("test", params)35 //把事件分派出去36 dispatcher.DispatchEvent(event)37 38 //移除监听139 dispatcher.RemoveEventListener("test", &fun1)40 41 //再把事件分派出去一次42 dispatcher.DispatchEvent(event)43 44 //因为主线程不会等子线程而直接关闭进程,这样会看不到效果,所以我在这里加了阻塞式延时45 time.Sleep(time.Second * 1)46 }47 48 //回调出得到的就是一个event对象了49 func (this *MClass) onTest(event *tbs.Event) {50 fmt.Println("onTest", event.Params["id"])51 }52 53 func (this *MClass) onTest2(event *tbs.Event) {54 fmt.Println("onTest2", event.Params["id"])55 }
输出结果:
add listener: testadd listener: testdispatch event: testonTest 1000remove listener: testdispatch event: testonTest2 1000onTest2 1000成功: 进程退出代码 0.
哈哈,成功地运行了。
demo你面的注释已经非常详尽了,看不懂就在下面问我好了!
昨晚我拿他来封装了一下golang的socket,改成了事件驱动,耦合度瞬间降低了很多。
1 func onServerStarted(event *tbs.Event) { 2 fmt.Println("server started.") 3 } 4 5 func onAccept(event *tbs.Event) { 6 socket := (event.Params["socket"]).(*tbs.Socket) 7 8 fmt.Printf("client[#%d] connect on:%s\n", socket.Sign, socket.Conn.RemoteAddr().String()) 9 }10 11 func onData(event *tbs.Event) {12 socket := (event.Params["socket"]).(*tbs.Socket)13 bytes := (event.Params["bytes"]).([]byte)14 15 fmt.Printf("[#%d]:", socket.Sign)16 fmt.Println(bytes)17 }18 19 func onClosed(event *tbs.Event) {20 socket := (event.Params["socket"]).(*tbs.Socket)21 fmt.Printf("[#%d] closed\n", socket.Sign)22 }
我还是模仿了as3提供的socket,看如上四个回调,只要监听并开启了serversocket,那么我只要坐等这四个回调来处理游戏的逻辑即可,每个socket都绑有累加的Sign作为标识。
总结:
golang是一门不错的语言,特别灵活,反射也很方便,应该会火吧,希望国内能有更多golang的开发者社区能建立起来吧!
贴上我自己的博客地址: