Golang中使用heap编写一个简单高效的定时器模块

定时器模块在服务端开发中非常重要,一个高性能的定时器模块能够大幅度提升引擎的运行效率。使用Golang和heap实现一个通用的定时器模块,代码来自:https://github.com/xiaonanln/goTimer

也可以查看文档:http://godoc.org/github.com/xiaonanln/goTimer,下面是完整的代码,并进行适当的注释和分析。

从性能测试结果来看,基于heap的定时器模块在效率上并不会比时间轮(TimeWheel)的实现慢多少,但是逻辑上要简单很多。

源代码加注释:

  1. package timer
  2. import (
  3. "container/heap" // Golang提供的heap库
  4. "fmt"
  5. "os"
  6. "runtime/debug"
  7. "sync"
  8. "time"
  9. )
  10. const (
  11. MIN_TIMER_INTERVAL = 1 * time.Millisecond // 循环定时器的最小时间间隔
  12. )
  13. var (
  14. nextAddSeq uint = 1 // 用于为每个定时器对象生成一个唯一的递增的序号
  15. )
  16. // 定时器对象
  17. type Timer struct {
  18. fireTime time.Time // 触发时间
  19. interval time.Duration // 时间间隔(用于循环定时器)
  20. callback CallbackFunc // 回调函数
  21. repeat bool // 是否循环
  22. cancelled bool // 是否已经取消
  23. addseq uint // 序号
  24. }
  25. // 取消一个定时器,这个定时器将不会被触发
  26. func (t *Timer) Cancel() {
  27. t.cancelled = true
  28. }
  29. // 判断定时器是否已经取消
  30. func (t *Timer) IsActive() bool {
  31. return !t.cancelled
  32. }
  33. // 使用一个heap管理所有的定时器
  34. type _TimerHeap struct {
  35. timers []*Timer
  36. }
  37. // Golang要求heap必须实现下面这些函数,这些函数的含义都是不言自明的
  38. func (h *_TimerHeap) Len() int {
  39. return len(h.timers)
  40. }
  41. // 使用触发时间和需要对定时器进行比较
  42. func (h *_TimerHeap) Less(i, j int) bool {
  43. //log.Println(h.timers[i].fireTime, h.timers[j].fireTime)
  44. t1, t2 := h.timers[i].fireTime, h.timers[j].fireTime
  45. if t1.Before(t2) {
  46. return true
  47. }
  48. if t1.After(t2) {
  49. return false
  50. }
  51. // t1 == t2, making sure Timer with same deadline is fired according to their add order
  52. return h.timers[i].addseq < h.timers[j].addseq
  53. }
  54. func (h *_TimerHeap) Swap(i, j int) {
  55. var tmp *Timer
  56. tmp = h.timers[i]
  57. h.timers[i] = h.timers[j]
  58. h.timers[j] = tmp
  59. }
  60. func (h *_TimerHeap) Push(x interface{}) {
  61. h.timers = append(h.timers, x.(*Timer))
  62. }
  63. func (h *_TimerHeap) Pop() (ret interface{}) {
  64. l := len(h.timers)
  65. h.timers, ret = h.timers[:l-1], h.timers[l-1]
  66. return
  67. }
  68. // 定时器回调函数的类型定义
  69. type CallbackFunc func()
  70. var (
  71. timerHeap _TimerHeap // 定时器heap对象
  72. timerHeapLock sync.Mutex // 一个全局的锁
  73. )
  74. func init() {
  75. heap.Init(&timerHeap) // 初始化定时器heap
  76. }
  77. // 设置一个一次性的回调,这个回调将在d时间后触发,并调用callback函数
  78. func AddCallback(d time.Duration, callback CallbackFunc) *Timer {
  79. t := &Timer{
  80. fireTime: time.Now().Add(d),
  81. interval: d,
  82. callback: callback,
  83. repeat: false,
  84. }
  85. timerHeapLock.Lock() // 使用锁规避竞争条件
  86. t.addseq = nextAddSeq
  87. nextAddSeq += 1
  88. heap.Push(&timerHeap, t)
  89. timerHeapLock.Unlock()
  90. return t
  91. }
  92. // 设置一个定时触发的回调,这个回调将在d时间后第一次触发,以后每隔d时间重复触发,并调用callback函数
  93. func AddTimer(d time.Duration, callback CallbackFunc) *Timer {
  94. if d < MIN_TIMER_INTERVAL {
  95. d = MIN_TIMER_INTERVAL
  96. }
  97. t := &Timer{
  98. fireTime: time.Now().Add(d),
  99. interval: d,
  100. callback: callback,
  101. repeat: true, // 设置为循环定时器
  102. }
  103. timerHeapLock.Lock()
  104. t.addseq = nextAddSeq // set addseq when locked
  105. nextAddSeq += 1
  106. heap.Push(&timerHeap, t)
  107. timerHeapLock.Unlock()
  108. return t
  109. }
  110. // 对定时器模块进行一次Tick
  111. //
  112. // 一般上层模块需要在一个主线程的goroutine里按一定的时间间隔不停的调用Tick函数,从而确保timer能够按时触发,并且
  113. // 所有Timer的回调函数也在这个goroutine里运行。
  114. func Tick() {
  115. now := time.Now()
  116. timerHeapLock.Lock()
  117. for {
  118. if timerHeap.Len() <= 0 { // 没有任何定时器,立刻返回
  119. break
  120. }
  121. nextFireTime := timerHeap.timers[0].fireTime
  122. if nextFireTime.After(now) { // 没有到时间的定时器,返回
  123. break
  124. }
  125. t := heap.Pop(&timerHeap).(*Timer)
  126. if t.cancelled { // 忽略已经取消的定时器
  127. continue
  128. }
  129. if !t.repeat {
  130. t.cancelled = true
  131. }
  132. <strong> // 必须先解锁,然后再调用定时器的回调函数,否则可能导致死锁!!!</strong>
  133. timerHeapLock.Unlock()
  134. runCallback(t.callback) // 运行回调函数并捕获panic
  135. timerHeapLock.Lock()
  136. if t.repeat { // 如果是循环timer就把Timer重新放回heap中
  137. // add Timer back to heap
  138. t.fireTime = t.fireTime.Add(t.interval)
  139. if !t.fireTime.After(now) {
  140. t.fireTime = now.Add(t.interval)
  141. }
  142. t.addseq = nextAddSeq
  143. nextAddSeq += 1
  144. heap.Push(&timerHeap, t)
  145. }
  146. }
  147. timerHeapLock.Unlock()
  148. }
  149. // 创建一个goroutine对定时器模块进行定时的Tick
  150. func StartTicks(tickInterval time.Duration) {
  151. go selfTickRoutine(tickInterval)
  152. }
  153. func selfTickRoutine(tickInterval time.Duration) {
  154. for {
  155. time.Sleep(tickInterval)
  156. Tick()
  157. }
  158. }
  159. // 运行定时器的回调函数,并捕获panic,将panic转化为错误输出
  160. func runCallback(callback CallbackFunc) {
  161. defer func() {
  162. err := recover()
  163. if err != nil {
  164. fmt.Fprintf(os.Stderr, "Callback %v paniced: %v\n", callback, err)
  165. debug.PrintStack()
  166. }
  167. }()
  168. callback()
  169. }
ft_authoradmin  ft_create_time2017-08-03 21:18
 ft_update_time2017-10-29 14:42