定時器模塊在服務端開發中很是重要,一個高性能的定時器模塊可以大幅度提高引擎的運行效率。使用Golang和heap實現一個通用的定時器模塊,代碼來自:https://github.com/xiaonanln/goTimergit
也能夠查看文檔:http://godoc.org/github.com/xiaonanln/goTimer,下面是完整的代碼,並進行適當的註釋和分析。github
從性能測試結果來看,基於heap的定時器模塊在效率上並不會比時間輪(TimeWheel)的實現慢多少,可是邏輯上要簡單不少。app
package timer import ( "container/heap" // Golang提供的heap庫 "fmt" "os" "runtime/debug" "sync" "time" ) const ( MIN_TIMER_INTERVAL = 1 * time.Millisecond // 循環定時器的最小時間間隔 ) var ( nextAddSeq uint = 1 // 用於爲每一個定時器對象生成一個惟一的遞增的序號 ) // 定時器對象 type Timer struct { fireTime time.Time // 觸發時間 interval time.Duration // 時間間隔(用於循環定時器) callback CallbackFunc // 回調函數 repeat bool // 是否循環 cancelled bool // 是否已經取消 addseq uint // 序號 } // 取消一個定時器,這個定時器將不會被觸發 func (t *Timer) Cancel() { t.cancelled = true } // 判判定時器是否已經取消 func (t *Timer) IsActive() bool { return !t.cancelled } // 使用一個heap管理全部的定時器 type _TimerHeap struct { timers []*Timer } // Golang要求heap必須實現下面這些函數,這些函數的含義都是不言自明的 func (h *_TimerHeap) Len() int { return len(h.timers) } // 使用觸發時間和須要對定時器進行比較 func (h *_TimerHeap) Less(i, j int) bool { //log.Println(h.timers[i].fireTime, h.timers[j].fireTime) t1, t2 := h.timers[i].fireTime, h.timers[j].fireTime if t1.Before(t2) { return true } if t1.After(t2) { return false } // t1 == t2, making sure Timer with same deadline is fired according to their add order return h.timers[i].addseq < h.timers[j].addseq } func (h *_TimerHeap) Swap(i, j int) { var tmp *Timer tmp = h.timers[i] h.timers[i] = h.timers[j] h.timers[j] = tmp } func (h *_TimerHeap) Push(x interface{}) { h.timers = append(h.timers, x.(*Timer)) } func (h *_TimerHeap) Pop() (ret interface{}) { l := len(h.timers) h.timers, ret = h.timers[:l-1], h.timers[l-1] return } // 定時器回調函數的類型定義 type CallbackFunc func() var ( timerHeap _TimerHeap // 定時器heap對象 timerHeapLock sync.Mutex // 一個全局的鎖 ) func init() { heap.Init(&timerHeap) // 初始化定時器heap } // 設置一個一次性的回調,這個回調將在d時間後觸發,並調用callback函數 func AddCallback(d time.Duration, callback CallbackFunc) *Timer { t := &Timer{ fireTime: time.Now().Add(d), interval: d, callback: callback, repeat: false, } timerHeapLock.Lock() // 使用鎖規避競爭條件 t.addseq = nextAddSeq nextAddSeq += 1 heap.Push(&timerHeap, t) timerHeapLock.Unlock() return t } // 設置一個定時觸發的回調,這個回調將在d時間後第一次觸發,之後每隔d時間重複觸發,並調用callback函數 func AddTimer(d time.Duration, callback CallbackFunc) *Timer { if d < MIN_TIMER_INTERVAL { d = MIN_TIMER_INTERVAL } t := &Timer{ fireTime: time.Now().Add(d), interval: d, callback: callback, repeat: true, // 設置爲循環定時器 } timerHeapLock.Lock() t.addseq = nextAddSeq // set addseq when locked nextAddSeq += 1 heap.Push(&timerHeap, t) timerHeapLock.Unlock() return t } // 對定時器模塊進行一次Tick // // 通常上層模塊須要在一個主線程的goroutine裏按必定的時間間隔不停的調用Tick函數,從而確保timer可以按時觸發,而且 // 全部Timer的回調函數也在這個goroutine裏運行。 func Tick() { now := time.Now() timerHeapLock.Lock() for { if timerHeap.Len() <= 0 { // 沒有任何定時器,馬上返回 break } nextFireTime := timerHeap.timers[0].fireTime if nextFireTime.After(now) { // 沒有到時間的定時器,返回 break } t := heap.Pop(&timerHeap).(*Timer) if t.cancelled { // 忽略已經取消的定時器 continue } if !t.repeat { t.cancelled = true } <strong> // 必須先解鎖,而後再調用定時器的回調函數,不然可能致使死鎖!!!</strong> timerHeapLock.Unlock() runCallback(t.callback) // 運行回調函數並捕獲panic timerHeapLock.Lock() if t.repeat { // 若是是循環timer就把Timer從新放回heap中 // add Timer back to heap t.fireTime = t.fireTime.Add(t.interval) if !t.fireTime.After(now) { t.fireTime = now.Add(t.interval) } t.addseq = nextAddSeq nextAddSeq += 1 heap.Push(&timerHeap, t) } } timerHeapLock.Unlock() } // 建立一個goroutine對定時器模塊進行定時的Tick func StartTicks(tickInterval time.Duration) { go selfTickRoutine(tickInterval) } func selfTickRoutine(tickInterval time.Duration) { for { time.Sleep(tickInterval) Tick() } } // 運行定時器的回調函數,並捕獲panic,將panic轉化爲錯誤輸出 func runCallback(callback CallbackFunc) { defer func() { err := recover() if err != nil { fmt.Fprintf(os.Stderr, "Callback %v paniced: %v\n", callback, err) debug.PrintStack() } }() callback() }