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  }