github.com/suiyunonghen/dxcommonlib@v0.0.0-20190612012041-7f8547a81a67/TimeWheelingWorker.go (about)

     1  /*
     2  时间轮询调度池,只用一个定时器来实现After等超时设定,默认轮渡器设定为1个小时,精度为500毫秒
     3  如果要使用更精确的定时器,请使用NewTimeWheelWorker自己指定定时器时间,目前在我的电脑上测试来看,最精确能到2毫秒
     4  Autor: 不得闲
     5  QQ:75492895
     6  */
     7  package DxCommonLib
     8  
     9  import (
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  )
    14  
    15  type (
    16  
    17  	//每个槽中的记录对象
    18  	slotRecord struct {
    19  		curWheelIndex		int			//当前轮询的索引
    20  		wheelCount			int			//需要轮询多少次触发
    21  		notifychan			chan struct{} //通知
    22  		next				*slotRecord	//下一个轮询点
    23  	}
    24  
    25  	TimeWheelWorker struct {
    26  		sync.Mutex                 //调度锁
    27  		ticker     *time.Ticker    //调度器时钟
    28  		timeslocks []*slotRecord   //时间槽
    29  		slockcount int
    30  		maxTimeout time.Duration
    31  		quitchan   chan struct{}
    32  		curindex   int //当前的索引
    33  		interval   time.Duration
    34  		tkfunc		func()
    35  		recordPool	sync.Pool
    36  	}
    37  )
    38  
    39  
    40  var (
    41  	defaultTimeWheelWorker *TimeWheelWorker
    42      coarseTime 				atomic.Value		//存放的是当前的实际时间
    43  )
    44  
    45  //interval指定调度的时间间隔
    46  //slotBlockCount指定时间轮的块长度
    47  func NewTimeWheelWorker(interval time.Duration, slotBlockCount int,tkfunc func()) *TimeWheelWorker {
    48  	result := new(TimeWheelWorker)
    49  	result.interval = interval
    50  	result.quitchan = make(chan struct{})
    51  	result.slockcount = slotBlockCount
    52  	result.tkfunc = tkfunc
    53  	result.maxTimeout = interval * time.Duration(slotBlockCount)
    54  	result.timeslocks = make([]*slotRecord, slotBlockCount)
    55  	result.ticker = time.NewTicker(interval)
    56  	go result.run()
    57  	return result
    58  }
    59  
    60  func (worker *TimeWheelWorker) run() {
    61  	for {
    62  		select {
    63  		case <-worker.ticker.C:
    64  			//执行定时操作
    65  			//获取当前的时间槽数据
    66  			worker.Lock()
    67  			lastrec := worker.timeslocks[worker.curindex]
    68  			if lastrec != nil{
    69  				var firstrec *slotRecord
    70  				for{
    71  					currec := lastrec.next
    72  					lastrec.curWheelIndex++
    73  					if lastrec.curWheelIndex >= lastrec.wheelCount{ //断开
    74  						worker.freeRecord(lastrec)
    75  					}else if firstrec == nil{
    76  						firstrec = lastrec //插入的时候就直接按照wheelCount大小排序了,只用增加一个个的序号就行了
    77  						for currec != nil{
    78  							currec.curWheelIndex++
    79  							currec = currec.next
    80  						}
    81  						break
    82  					}/*else{
    83  						firstrec.next = lastrec
    84  					}*/
    85  					if currec == nil{
    86  						break
    87  					}
    88  					lastrec = currec
    89  				}
    90  				worker.timeslocks[worker.curindex] = firstrec
    91  			}
    92  			worker.curindex = (worker.curindex + 1) % worker.slockcount
    93  			worker.Unlock()
    94  			if worker.tkfunc!=nil{
    95  				worker.tkfunc()
    96  			}
    97  		case <-worker.quitchan:
    98  			worker.ticker.Stop()
    99  			return
   100  		}
   101  	}
   102  }
   103  
   104  func (worker *TimeWheelWorker) Stop() {
   105  	close(worker.quitchan)
   106  }
   107  
   108  func (worker *TimeWheelWorker)getRecord(wheelcount int)*slotRecord  {
   109  	var result *slotRecord
   110  	v := worker.recordPool.Get()
   111  	if v!=nil{
   112  		result = v.(*slotRecord)
   113  	}else{
   114  		result = new(slotRecord)
   115  	}
   116  	result.curWheelIndex = 0
   117  	result.wheelCount = wheelcount
   118  	result.notifychan = make(chan struct{})
   119  	result.next = nil
   120  	return result
   121  }
   122  
   123  func (worker *TimeWheelWorker)freeRecord(rec *slotRecord)  {
   124  	rec.next = nil
   125  	close(rec.notifychan)
   126  	rec.notifychan = nil
   127  	rec.wheelCount = 0
   128  	rec.curWheelIndex = 0
   129  	worker.recordPool.Put(rec)
   130  }
   131  
   132  func (worker *TimeWheelWorker) After(d time.Duration) <-chan struct{} {
   133  	index := int(d / worker.interval) //触发多少次到
   134  	wheelcount := int(index / worker.slockcount)
   135  	if index % worker.slockcount > 0{
   136  		wheelcount++
   137  	}
   138  	if index > 0 {
   139  		index--
   140  	}
   141  	worker.Lock()
   142  	index = (worker.curindex + index) % worker.slockcount
   143  	rec := worker.timeslocks[index]
   144  	if rec == nil {
   145  		rec = worker.getRecord(wheelcount)
   146  		worker.timeslocks[index] = rec
   147  	}else{ //查找对应的位置
   148  		var last *slotRecord=nil
   149  		for{
   150  			currec := rec.next
   151  			if wheelcount < rec.wheelCount{
   152  				currec = worker.getRecord(wheelcount)
   153  				currec.next = rec
   154  				if last == nil{
   155  					worker.timeslocks[index] = currec
   156  				}else{
   157  					last.next = currec
   158  				}
   159  				rec = currec
   160  				break
   161  			}else if wheelcount == rec.wheelCount{ //已经存在,直接退出
   162  				break
   163  			}else if currec == nil{
   164  				currec = worker.getRecord(wheelcount) //链接一个新的
   165  				rec.next = currec
   166  				rec = currec
   167  				break
   168  			}
   169  			last = rec
   170  			rec = currec
   171  		}
   172  	}
   173  	worker.Unlock()
   174  	return rec.notifychan
   175  }
   176  
   177  func (worker *TimeWheelWorker)AfterFunc(d time.Duration,afunc func())  {
   178  	select{
   179  	case <-worker.After(d):
   180  		afunc()
   181  	}
   182  }
   183  
   184  func (worker *TimeWheelWorker) Sleep(d time.Duration) {
   185  	select{
   186  	case <-worker.After(d):
   187  		return
   188  	}
   189  }
   190  
   191  func After(d time.Duration) <-chan struct{} {
   192  	return defaultTimeWheelWorker.After(d)
   193  }
   194  
   195  func AfterFunc(d time.Duration,afunc func()) {
   196  	defaultTimeWheelWorker.AfterFunc(d,afunc)
   197  }
   198  
   199  func Sleep(d time.Duration) {
   200  	defaultTimeWheelWorker.Sleep(d)
   201  }
   202  
   203  func ReSetDefaultTimeWheel(Chkinterval time.Duration,slotBlockCount int){
   204  	if defaultTimeWheelWorker.interval != Chkinterval  ||
   205  		defaultTimeWheelWorker.slockcount != slotBlockCount{
   206  			defaultTimeWheelWorker.Stop()
   207  			defaultTimeWheelWorker = NewTimeWheelWorker(Chkinterval, slotBlockCount, func() {
   208  				t := time.Now().Truncate(Chkinterval)
   209  				coarseTime.Store(&t)
   210  			})
   211  	}
   212  }
   213  
   214  
   215  func init()  {
   216  	defaultTimeWheelWorker = NewTimeWheelWorker(time.Millisecond*500, 7200, func() {
   217  		t := time.Now().Truncate(time.Millisecond*500)
   218  		coarseTime.Store(&t)
   219  	})
   220  	t := time.Now().Truncate(time.Millisecond*500)
   221  	coarseTime.Store(&t)
   222  }
   223  
   224  func CoarseTimeNow() time.Time {
   225  	tp := coarseTime.Load().(*time.Time)
   226  	return *tp
   227  }
   228