github.com/suiyunonghen/DxCommonLib@v0.5.3/TimeWheelingWorker.go (about) 1 // Package DxCommonLib 2 /* 3 时间轮询调度池,只用一个定时器来实现After等超时设定,默认轮渡器设定为1个小时,精度为500毫秒 4 如果要使用更精确的定时器,请使用NewTimeWheelWorker自己指定定时器时间,目前在我的电脑上测试来看,最精确能到2毫秒 5 Author: 不得闲 6 QQ:75492895 7 */ 8 package DxCommonLib 9 10 import ( 11 "sync" 12 "sync/atomic" 13 "time" 14 ) 15 16 type ( 17 //每个槽中的记录对象 18 slotRecord struct { 19 notifyCount int32 //要通知多少个 20 wheelCount int32 //需要轮询多少圈触发 21 curWheelIndex int32 //当前轮询的圈索引 22 slotTask []defTaskRunner //能执行的任务信息 23 mu sync.Mutex 24 notifychan chan struct{} //通知 25 next *slotRecord //下一个轮询点 26 } 27 28 TimeWheelWorker struct { 29 sync.Mutex //调度锁 30 curindex int32 //当前的索引 31 slockcount int32 32 ticker *time.Ticker //调度器时钟 33 timeslocks []*slotRecord //时间槽 34 maxTimeout time.Duration 35 quitchan chan struct{} 36 interval time.Duration 37 recordPool sync.Pool 38 } 39 ) 40 41 var ( 42 defaultTimeWheelWorker *TimeWheelWorker 43 minTickerInterval time.Duration 44 ) 45 46 func init() { 47 //获取一下最准确的精度 48 minTickerInterval = time.Millisecond * 2 49 ticker := time.NewTicker(minTickerInterval) 50 start := time.Now() 51 for i := 0; i < 9; i++ { 52 <-ticker.C 53 } 54 cur := <-ticker.C 55 ticker.Stop() 56 minTickerInterval = cur.Sub(start) / time.Duration(10) 57 } 58 59 // NewTimeWheelWorker 60 // interval指定调度的时间间隔 61 // slotBlockCount指定时间轮的块长度 62 func NewTimeWheelWorker(interval time.Duration, slotBlockCount int32) *TimeWheelWorker { 63 if interval < minTickerInterval { 64 interval = minTickerInterval 65 } 66 result := new(TimeWheelWorker) 67 result.interval = interval 68 result.quitchan = make(chan struct{}) 69 result.slockcount = slotBlockCount 70 result.maxTimeout = interval * time.Duration(slotBlockCount) 71 result.timeslocks = make([]*slotRecord, slotBlockCount) 72 result.ticker = time.NewTicker(interval) 73 go result.run() 74 return result 75 } 76 77 func (worker *TimeWheelWorker) run() { 78 runAfter := func(data ...interface{}) { 79 slotTaskRunner := data[0].([]defTaskRunner) 80 for i := range slotTaskRunner { 81 slotTaskRunner[i].runFunc(slotTaskRunner[i].runArgs...) 82 } 83 } 84 for { 85 select { 86 case <-worker.ticker.C: 87 var slotTaskRunner []defTaskRunner 88 worker.Lock() 89 lastRec := worker.timeslocks[worker.curindex] 90 if lastRec != nil { 91 var firstRec *slotRecord 92 for { 93 curRec := lastRec.next 94 lastRec.curWheelIndex++ 95 if lastRec.curWheelIndex >= lastRec.wheelCount { //到时间了,释放掉 96 /*for{ 97 select{ 98 case <-worker.After(20): 99 //这个After就可能会被丢弃,所以实际的通知数量可能不会有设定的个数大小 100 case <-chan2: 101 default: 102 } 103 }*/ 104 //通知多少次,实际的通知次数可能会比这个设定的次数小 105 notifyCount := int(atomic.SwapInt32(&lastRec.notifyCount, 0)) 106 for i := 0; i < notifyCount; i++ { 107 select { 108 case lastRec.notifychan <- struct{}{}: 109 //通知成功 110 default: 111 break 112 } 113 } 114 slotTaskRunner = append(slotTaskRunner, lastRec.slotTask...) 115 for i := range lastRec.slotTask { 116 lastRec.slotTask[i].runFunc = nil 117 lastRec.slotTask[i].runArgs = nil 118 } 119 lastRec.next = nil 120 lastRec.slotTask = lastRec.slotTask[:0] 121 lastRec.wheelCount = 0 122 lastRec.curWheelIndex = 0 123 worker.recordPool.Put(lastRec) 124 } else if firstRec == nil { 125 firstRec = lastRec //插入的时候就直接按照wheelCount大小排序了,只用增加一个个的序号就行了 126 for curRec != nil { 127 curRec.curWheelIndex++ //圈索引增加 128 curRec = curRec.next 129 } 130 break 131 } 132 if curRec == nil { 133 break 134 } 135 lastRec = curRec 136 } 137 worker.timeslocks[worker.curindex] = firstRec 138 } 139 worker.curindex++ 140 if worker.curindex == worker.slockcount { 141 worker.curindex = 0 142 } 143 worker.Unlock() 144 if len(slotTaskRunner) > 0 { 145 MustRunAsync(runAfter, slotTaskRunner) 146 } 147 case <-worker.quitchan: 148 worker.ticker.Stop() 149 return 150 } 151 } 152 } 153 154 func (worker *TimeWheelWorker) Stop() { 155 close(worker.quitchan) 156 } 157 158 func (worker *TimeWheelWorker) getRecord(wheelcount int32) *slotRecord { 159 var result *slotRecord 160 v := worker.recordPool.Get() 161 if v != nil { 162 result = v.(*slotRecord) 163 } else { 164 result = new(slotRecord) 165 result.notifychan = make(chan struct{}) 166 result.slotTask = make([]defTaskRunner, 0, 8) 167 } 168 result.curWheelIndex = 0 169 result.notifyCount = 0 170 result.wheelCount = wheelcount 171 result.next = nil 172 return result 173 } 174 175 func (worker *TimeWheelWorker) after(d time.Duration) *slotRecord { 176 index := int32(d / worker.interval) //触发多少次到 177 wheelCount := index / worker.slockcount //轮询多少圈 178 if index%worker.slockcount > 0 { 179 wheelCount++ 180 } 181 if index > 0 { 182 index-- 183 } 184 worker.Lock() 185 index = (worker.curindex + index) % worker.slockcount 186 rec := worker.timeslocks[index] 187 if rec == nil { 188 rec = worker.getRecord(wheelCount) 189 worker.timeslocks[index] = rec 190 } else { //查找对应的位置 191 var last *slotRecord = nil 192 for { 193 curRec := rec.next 194 if wheelCount < rec.wheelCount { 195 //在当前圈前面,时间靠前插入 196 curRec = worker.getRecord(wheelCount) 197 curRec.next = rec 198 if last == nil { 199 worker.timeslocks[index] = curRec 200 } else { 201 last.next = curRec 202 } 203 rec = curRec 204 break 205 } else if wheelCount == rec.wheelCount { //已经存在,直接退出 206 break 207 } else if curRec == nil { 208 curRec = worker.getRecord(wheelCount) //链接一个新的 209 rec.next = curRec 210 rec = curRec 211 break 212 } 213 last = rec 214 rec = curRec 215 } 216 } 217 worker.Unlock() 218 return rec 219 } 220 221 func (worker *TimeWheelWorker) After(d time.Duration) <-chan struct{} { 222 rec := worker.after(d) 223 atomic.AddInt32(&rec.notifyCount, 1) 224 return rec.notifychan 225 } 226 227 func (worker *TimeWheelWorker) AfterFunc(d time.Duration, afterFunc GWorkerFunc, data ...interface{}) { 228 rec := worker.after(d) 229 rec.mu.Lock() 230 rec.slotTask = append(rec.slotTask, defTaskRunner{ 231 runFunc: afterFunc, 232 runArgs: data, 233 }) 234 rec.mu.Unlock() 235 } 236 237 func (worker *TimeWheelWorker) Sleep(d time.Duration) { 238 <-worker.After(d) 239 } 240 241 func After(d time.Duration) <-chan struct{} { 242 if defaultTimeWheelWorker == nil { 243 defaultTimeWheelWorker = NewTimeWheelWorker(time.Millisecond*500, 7200) 244 } 245 return defaultTimeWheelWorker.After(d) 246 } 247 248 func AfterFunc(d time.Duration, aFunc GWorkerFunc) { 249 if defaultTimeWheelWorker == nil { 250 defaultTimeWheelWorker = NewTimeWheelWorker(time.Millisecond*500, 7200) 251 } 252 defaultTimeWheelWorker.AfterFunc(d, aFunc) 253 } 254 255 func Sleep(d time.Duration) { 256 if defaultTimeWheelWorker == nil { 257 defaultTimeWheelWorker = NewTimeWheelWorker(time.Millisecond*500, 7200) 258 } 259 defaultTimeWheelWorker.Sleep(d) 260 } 261 262 func ReSetDefaultTimeWheel(Chkinterval time.Duration, slotBlockCount int32) { 263 if Chkinterval < minTickerInterval { 264 Chkinterval = minTickerInterval 265 } 266 if defaultTimeWheelWorker == nil { 267 defaultTimeWheelWorker = NewTimeWheelWorker(Chkinterval, slotBlockCount) 268 return 269 } 270 if defaultTimeWheelWorker.interval != Chkinterval || 271 defaultTimeWheelWorker.slockcount != slotBlockCount { 272 defaultTimeWheelWorker.Stop() 273 274 defaultTimeWheelWorker = NewTimeWheelWorker(Chkinterval, slotBlockCount) 275 } 276 }