github.com/suiyunonghen/dxcommonlib@v0.0.0-20190612012041-7f8547a81a67/GWorkers.go (about) 1 /* 2 从fasthttp中变更过来的GoRoutine池 3 Autor: 不得闲 4 QQ:75492895 5 */ 6 package DxCommonLib 7 8 import ( 9 "sync" 10 "runtime" 11 "time" 12 ) 13 14 type ( 15 //任务 16 ITaskRunner interface { 17 Run() 18 } 19 20 GWorkerFunc func(data ...interface{}) 21 GWorkers struct { 22 fMaxWorkersCount int //能同时存在的最多线程个数 23 fMaxWorkerIdleTime time.Duration //线程能空闲的最长时间,超过这个时间了会回收掉这个线程 24 ready []*workerChan //准备好的空闲线程 25 lock sync.Mutex 26 workerChanPool sync.Pool //工作者 27 deftaskrunnerPool sync.Pool //默认任务 28 mustStop bool 29 fstopchan chan struct{} 30 workersCount int 31 } 32 33 workerChan struct { 34 lastUseTime time.Time 35 fOwner *GWorkers 36 fcurTask chan ITaskRunner 37 } 38 39 defTaskRunner struct { 40 runfunc GWorkerFunc 41 runargs []interface{} 42 } 43 ) 44 45 func (runner *defTaskRunner) Run() { 46 runner.runfunc(runner.runargs...) 47 } 48 49 func (workers *GWorkers) Start() { 50 if workers.fstopchan != nil { 51 panic("BUG: GWorkers already started") 52 } 53 workers.fstopchan = make(chan struct{}) 54 stopCh := workers.fstopchan 55 go func() { 56 var scratch []*workerChan 57 for { 58 select { 59 case <-stopCh: 60 return 61 case <-After(workers.fMaxWorkerIdleTime): 62 workers.clean(&scratch)//定时执行清理回收线程 63 } 64 } 65 }() 66 } 67 68 69 func (workers *GWorkers) Stop() { 70 if workers.fstopchan == nil { 71 panic("BUG: GWorkers wasn't started") 72 } 73 close(workers.fstopchan) 74 workers.fstopchan = nil 75 76 // Stop all the workers waiting for incoming connections. 77 // Do not wait for busy workers - they will stop after 78 // serving the connection and noticing wp.mustStop = true. 79 workers.lock.Lock() 80 ready := workers.ready 81 for i, ch := range ready { 82 ch.fcurTask <- nil 83 ready[i] = nil 84 } 85 workers.ready = ready[:0] 86 workers.mustStop = true 87 workers.lock.Unlock() 88 } 89 90 91 var workerChanCap = func() int { 92 // Use blocking workerChan if GOMAXPROCS=1. 93 // This immediately switches Serve to WorkerFunc, which results 94 // in higher performance (under go1.5 at least). 95 if runtime.GOMAXPROCS(0) == 1 { 96 return 0 97 } 98 99 // Use non-blocking workerChan if GOMAXPROCS>1, 100 // since otherwise the Serve caller (Acceptor) may lag accepting 101 // new connections if WorkerFunc is CPU-bound. 102 return 1 103 }() 104 105 func (workers *GWorkers) clean(scratch *[]*workerChan) { 106 maxIdleWorkerDuration := workers.fMaxWorkerIdleTime 107 108 // Clean least recently used workers if they didn't serve connections 109 // for more than maxIdleWorkerDuration. 110 currentTime := time.Now() 111 workers.lock.Lock() 112 ready := workers.ready 113 n := len(ready) 114 i := 0 115 //超过设定的最大空闲时间的,就解雇掉 116 for i < n && currentTime.Sub(ready[i].lastUseTime) > maxIdleWorkerDuration { 117 i++ 118 } 119 *scratch = append((*scratch)[:0], ready[:i]...) 120 if i > 0 { 121 m := copy(ready, ready[i:]) 122 for i = m; i < n; i++ { 123 ready[i] = nil 124 } 125 workers.ready = ready[:m] 126 } 127 workers.lock.Unlock() 128 129 // Notify obsolete workers to stop. 130 // This notification must be outside the wp.lock, since ch.ch 131 // may be blocking and may consume a lot of time if many workers 132 // are located on non-local CPUs. 133 tmp := *scratch 134 for i, ch := range tmp { 135 ch.fcurTask <- nil 136 tmp[i] = nil 137 } 138 } 139 140 func (workers *GWorkers) getCh() *workerChan { 141 var ch *workerChan 142 createWorker := false 143 144 workers.lock.Lock() 145 ready := workers.ready 146 n := len(ready) - 1 147 if n < 0 { 148 if workers.workersCount < workers.fMaxWorkersCount { 149 createWorker = true 150 workers.workersCount++ 151 } 152 } else { 153 ch = ready[n] 154 ready[n] = nil 155 workers.ready = ready[:n] 156 } 157 workers.lock.Unlock() 158 159 if ch == nil { 160 if !createWorker { 161 return nil 162 } 163 vch := workers.workerChanPool.Get() 164 if vch == nil { 165 vch = &workerChan{ 166 fOwner:workers, 167 fcurTask: make(chan ITaskRunner, workerChanCap), 168 } 169 } 170 ch = vch.(*workerChan) 171 go func() { 172 workers.workerFunc(ch) 173 workers.workerChanPool.Put(vch) 174 }() 175 } 176 return ch 177 } 178 179 func (workers *GWorkers) release(ch *workerChan) bool { 180 ch.lastUseTime = CoarseTimeNow() 181 workers.lock.Lock() 182 if workers.mustStop { 183 workers.lock.Unlock() 184 return false 185 } 186 workers.ready = append(workers.ready, ch) 187 workers.lock.Unlock() 188 return true 189 } 190 191 192 func (workers *GWorkers) workerFunc(ch *workerChan) { 193 for curTask := range ch.fcurTask { 194 if curTask == nil { 195 break 196 } 197 curTask.Run() //执行 198 //回收curtask 199 switch runner := curTask.(type) { 200 case *defTaskRunner: 201 workers.deftaskrunnerPool.Put(runner) 202 } 203 if !workers.release(ch) { 204 break 205 } 206 } 207 208 workers.lock.Lock() 209 workers.workersCount-- 210 workers.lock.Unlock() 211 } 212 213 func (workers *GWorkers)PostFunc(routineFunc GWorkerFunc,params ...interface{}) { 214 wch := workers.getCh() 215 if wch != nil { 216 taskrunner := workers.deftaskrunnerPool.Get().(*defTaskRunner) 217 taskrunner.runfunc = routineFunc 218 taskrunner.runargs = params 219 wch.fcurTask <- taskrunner 220 } 221 } 222 223 func (workers *GWorkers)Post(runner ITaskRunner) { 224 wch := workers.getCh() 225 if wch != nil { 226 wch.fcurTask <- runner 227 } 228 } 229 230 func NewWorkers(maxGoroutinesAmount int, maxGoroutineIdleDuration time.Duration) *GWorkers { 231 gp := new(GWorkers) 232 gp.deftaskrunnerPool.New = func() interface{} { 233 return new(defTaskRunner) 234 } 235 if maxGoroutinesAmount <= 0 { 236 gp.fMaxWorkersCount = 256 * 1024 237 } else { 238 gp.fMaxWorkersCount = maxGoroutinesAmount 239 } 240 if maxGoroutineIdleDuration <= 0 { 241 gp.fMaxWorkerIdleTime = 15 * time.Second 242 } else { 243 gp.fMaxWorkerIdleTime = maxGoroutineIdleDuration 244 } 245 gp.Start() 246 return gp 247 } 248 249 var defWorkers *GWorkers 250 251 func PostFunc(routineFunc GWorkerFunc,params ...interface{}) { 252 if defWorkers == nil{ 253 defWorkers = NewWorkers(0,0) 254 } 255 defWorkers.PostFunc(routineFunc,params...) 256 } 257 258 func Post(runner ITaskRunner) { 259 if defWorkers == nil{ 260 defWorkers = NewWorkers(0,0) 261 } 262 defWorkers.Post(runner) 263 } 264 265 func StopWorkers() { 266 if defWorkers != nil{ 267 defWorkers.Stop() 268 } 269 }