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 }