github.com/JohanShen/go-utils@v1.1.4-0.20201117124024-901319a2b2a0/timer/timer.go (about)

     1  package timer
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/JohanShen/go-utils/logger"
     7  	"math/rand"
     8  	"reflect"
     9  	"runtime"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  func init() {}
    15  
    16  // 事件回调时回传的参数
    17  type EventArg struct {
    18  	Sender *Timer
    19  	Msg    string
    20  }
    21  
    22  // 事件
    23  type Event struct {
    24  	events []func(*EventArg) error
    25  }
    26  
    27  // 绑定事件
    28  // 支持链式绑定
    29  // timer.Bind(func1).Bind(func2)
    30  func (e *Event) Bind(fun func(*EventArg) error) *Event {
    31  	e.events = append(e.events, fun)
    32  	return e
    33  }
    34  
    35  type Timer struct {
    36  	Elapsed   Event  //达到间隔时的回调
    37  	Name      string //名称
    38  	IsRunning bool   //运行状态
    39  
    40  	logger    logger.Logger // 日志记录器
    41  	mutex     *sync.Mutex   //互斥锁
    42  	interval  int64
    43  	state     chan int
    44  	startTime time.Time
    45  	stopTime  time.Time
    46  	wait      *sync.WaitGroup
    47  	ticker    *time.Ticker
    48  }
    49  
    50  // 创建新的实例化对象
    51  func NewTimer(interval int64) *Timer {
    52  	if interval <= 1 {
    53  		panic(errors.New("non-positive interval for NewTimer"))
    54  	}
    55  	name := fmt.Sprintf("%x", rand.Uint64())
    56  	obj := &Timer{interval: interval, Name: name, IsRunning: false}
    57  	obj.mutex = &sync.Mutex{}
    58  	obj.wait = &sync.WaitGroup{}
    59  	return obj
    60  }
    61  
    62  func initNewObj(obj *Timer) {
    63  	obj.stopTime = time.Unix(0, 0)
    64  	obj.ticker = time.NewTicker(time.Duration(obj.interval) * time.Millisecond)
    65  	obj.state = make(chan int, 1)
    66  }
    67  
    68  //设置日志
    69  func (obj *Timer) UseLogger(logger logger.Logger) *Timer {
    70  	obj.logger = logger
    71  	return obj
    72  }
    73  
    74  // 执行单个回调函数
    75  func execute(obj *Timer, fun func(*EventArg) error, now *time.Time) {
    76  
    77  	funcName := runtime.FuncForPC(reflect.ValueOf(fun).Pointer()).Name()
    78  
    79  	defer func() {
    80  		if err := recover(); err != nil {
    81  			logger.Panic(obj.logger, fmt.Sprintf("%s 回调错误 panic ", obj.Name), err)
    82  		}
    83  	}()
    84  
    85  	logger.Debug(obj.logger, fmt.Sprintf("%s 回调 %s", obj.Name, funcName))
    86  	arg := &EventArg{Sender: obj, Msg: fmt.Sprintf("回调执行时间:%s ", now)}
    87  	if err := fun(arg); err != nil {
    88  		logger.Error(obj.logger, fmt.Sprintf("%s 回调错误 error", obj.Name), logger.ArgAny("err", err))
    89  	}
    90  }
    91  
    92  func (obj *Timer) lisenTicker() {
    93  
    94  	logger.Debug(obj.logger, fmt.Sprintf("%s 准备监听", obj.Name))
    95  
    96  	defer func(obj *Timer) {
    97  		obj.wait.Done()
    98  		logger.Debug(obj.logger, fmt.Sprintf("%s 退出监听函数 ", obj.Name))
    99  	}(obj)
   100  
   101  	obj.wait.Add(1)
   102  
   103  Loop:
   104  
   105  	for {
   106  		select {
   107  		case now := <-obj.ticker.C:
   108  			//执行回调函数
   109  			logger.Debug(obj.logger, fmt.Sprintf("%s 回调函数-START[%d]", obj.Name, runtime.NumGoroutine()))
   110  			for _, f := range obj.Elapsed.events {
   111  				// 将最终的执行函数单独包装成方法
   112  				// 有利于其中一个或多个回调函数出错时,保证程序继续运行
   113  				go execute(obj, f, &now)
   114  			}
   115  			logger.Debug(obj.logger, fmt.Sprintf("%s 回调函数-END", obj.Name))
   116  		case state, ok := <-obj.state:
   117  			logger.Debug(obj.logger, fmt.Sprintf("%s 状态更改 state(value = %v, isclose = %v)", obj.Name, state, ok))
   118  			if !ok {
   119  				logger.Debug(obj.logger, fmt.Sprintf("%s 通道已被关闭", obj.Name))
   120  				break Loop
   121  			}
   122  			if state == 1 {
   123  				//obj.mutex.Lock()
   124  				logger.Debug(obj.logger, fmt.Sprintf("%s 收到关闭通知", obj.Name))
   125  				obj.wait.Done()
   126  
   127  				//obj.mutex.Unlock()
   128  				break Loop
   129  			}
   130  
   131  		}
   132  
   133  		logger.Debug(obj.logger, fmt.Sprintf("%s 准备接受下一轮信号", obj.Name))
   134  	}
   135  
   136  	logger.Debug(obj.logger, fmt.Sprintf("%s 完成监听", obj.Name))
   137  }
   138  
   139  // 设置定时器的名称
   140  func (obj *Timer) SetName(name string) *Timer {
   141  
   142  	logger.Debug(obj.logger, fmt.Sprintf("%s 更改名称 %[1]s => %s", obj.Name, name))
   143  	obj.Name = name
   144  	return obj
   145  }
   146  
   147  //设置执行间隔
   148  func (obj *Timer) SetInterval(interval int64) error {
   149  
   150  	if interval <= 1 {
   151  		return errors.New("non-positive interval for NewTimer")
   152  	}
   153  
   154  	obj.mutex.Lock()
   155  	obj.interval = interval
   156  	logger.Debug(obj.logger, fmt.Sprintf("%s 更改间隔 %[1]s -> %d", obj.Name, interval))
   157  	if obj.ticker != nil {
   158  		//fmt.Println()
   159  		//fmt.Print("set interval")
   160  		obj.ticker.Stop()
   161  		obj.wait.Add(1)
   162  		obj.state <- 1
   163  		obj.wait.Wait()
   164  		obj.ticker = time.NewTicker(time.Duration(obj.interval) * time.Millisecond)
   165  		//obj.state = make(chan int, 1)
   166  		//fmt.Print(" ok")
   167  	}
   168  	logger.Debug(obj.logger, fmt.Sprintf("%s 更改间隔成功 %[1]s -> %d", obj.Name, interval))
   169  	obj.mutex.Unlock()
   170  	if obj.IsRunning {
   171  		go obj.lisenTicker()
   172  	}
   173  	//fmt.Printf("%p", obj.ticker)
   174  	return nil
   175  }
   176  
   177  // 开始计时器
   178  // 需要通过 NewTimer 方法获取实例化
   179  func (obj *Timer) Start() {
   180  
   181  	// if obj.wait == nil {
   182  	// 	panic(errors.New("尚未初始化,请通过 New() 方法初始化对象。"))
   183  	// 	return
   184  	// }
   185  	if obj.IsRunning {
   186  		return
   187  	}
   188  
   189  	logger.Debug(obj.logger, fmt.Sprintf("%s 执行间隔 %d", obj.Name, obj.interval))
   190  
   191  	obj.mutex.Lock()
   192  	obj.startTime = time.Now()
   193  	obj.IsRunning = true
   194  
   195  	initNewObj(obj)
   196  
   197  	obj.mutex.Unlock()
   198  	//obj.wait.Add(1)
   199  	go obj.lisenTicker()
   200  	//obj.wait.Wait()
   201  }
   202  
   203  // 停止计时器
   204  func (obj *Timer) Stop() {
   205  
   206  	if obj.state == nil || obj.ticker == nil || !obj.IsRunning {
   207  		return
   208  	}
   209  
   210  	defer func() {
   211  		obj.mutex.Unlock()
   212  		if err := recover(); err != nil {
   213  			//
   214  			logger.Panic(obj.logger, fmt.Sprintf("%s 停止时出错", obj.Name), err)
   215  		} else {
   216  			//
   217  			logger.Debug(obj.logger, fmt.Sprintf("%s 停止成功", obj.Name))
   218  		}
   219  	}()
   220  
   221  	obj.mutex.Lock()
   222  	obj.stopTime = time.Now()
   223  	obj.wait.Add(1)
   224  
   225  	logger.Debug(obj.logger, fmt.Sprintf("%s 停止-START", obj.Name))
   226  	obj.ticker.Stop()
   227  	obj.state <- 1
   228  	obj.wait.Wait() //这个的作用就是确保协程退出后执行下面的代码
   229  	obj.IsRunning = false
   230  	close(obj.state)
   231  	logger.Debug(obj.logger, fmt.Sprintf("%s 停止-STOPPED", obj.Name))
   232  
   233  }