github.com/sandwich-go/boost@v1.3.29/xtime/wheel.go (about) 1 // wheel implements a time wheel algorithm that is suitable for large numbers of timers. 2 // It is based on golang channel broadcast mechanism. 3 4 package xtime 5 6 import ( 7 "sync" 8 "time" 9 ) 10 11 var ( 12 timerMap = NewWheelMap() 13 accuracy = 20 // means max 1/20 deviation 14 ) 15 16 func newWheelWithDuration(t time.Duration) *Wheel { 17 return NewWheel(t/time.Duration(accuracy), accuracy+1) 18 } 19 20 // WheelAfter 根据Duration复用时间轮 21 // Note: 22 // 内部会根据Duration创建时间轮, 相同Duration可以共用,这样带来的副作用就是如果时间不固定则会创建特别的多的时间轮 23 func WheelAfter(t time.Duration) <-chan struct{} { 24 w, _ := timerMap.GetOrSetFuncLock(t, newWheelWithDuration) 25 return w.After(t) 26 } 27 28 // SetAccuracy sets the accuracy for the timewheel. 29 // low accuracy usually have better performance. 30 func SetAccuracy(a int) { 31 accuracy = a 32 } 33 34 type Wheel struct { 35 sync.Mutex 36 interval time.Duration 37 ticker *time.Ticker 38 quit chan struct{} 39 maxTimeout time.Duration 40 cs []chan struct{} 41 pos int 42 } 43 44 func NewWheel(interval time.Duration, buckets int) *Wheel { 45 w := &Wheel{ 46 interval: interval, 47 quit: make(chan struct{}), 48 pos: 0, 49 maxTimeout: interval * (time.Duration(buckets - 1)), 50 cs: make([]chan struct{}, buckets), 51 ticker: time.NewTicker(interval), 52 } 53 for i := range w.cs { 54 w.cs[i] = make(chan struct{}) 55 } 56 go w.run() 57 return w 58 } 59 60 func (w *Wheel) Stop() { 61 close(w.quit) 62 } 63 64 // After 误差在一个interval内 65 // timeline : ---w.pos-1<--{x}-->call After()<--{y}-->w.pos----- 66 // x + y == interval, y 即是误差 67 // Note: 68 // 如果超过时间轮的最大值则使用最大值作为Timeout时间 69 func (w *Wheel) After(timeout time.Duration) <-chan struct{} { 70 if timeout > w.maxTimeout { 71 timeout = w.maxTimeout 72 } else if timeout < time.Second { 73 timeout = time.Second 74 } 75 76 w.Lock() 77 index := (w.pos + int(timeout/w.interval)) % len(w.cs) 78 b := w.cs[index] 79 w.Unlock() 80 return b 81 } 82 83 func (w *Wheel) run() { 84 for { 85 select { 86 case <-w.ticker.C: 87 w.onTicker() 88 case <-w.quit: 89 w.ticker.Stop() 90 return 91 } 92 } 93 } 94 95 func (w *Wheel) onTicker() { 96 w.Lock() 97 lastC := w.cs[w.pos] 98 w.cs[w.pos] = make(chan struct{}) 99 w.pos = (w.pos + 1) % len(w.cs) 100 w.Unlock() 101 close(lastC) 102 }