github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zpool/pool.go (about) 1 package zpool 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sync" 8 "time" 9 10 "github.com/sohaha/zlsgo/zdi" 11 "github.com/sohaha/zlsgo/zutil" 12 ) 13 14 type ( 15 // Task Define function callbacks 16 Task interface{} 17 taskfn func() error 18 WorkPool struct { 19 workers sync.Pool 20 injector zdi.Injector 21 queue chan *worker 22 usedNum *zutil.Int64 23 activeNum *zutil.Int64 24 panicFunc PanicFunc 25 New func() 26 minIdle uint 27 maxIdle uint 28 releaseTime time.Duration 29 mu sync.RWMutex 30 closed bool 31 } 32 worker struct { 33 jobQueue chan taskfn 34 stop chan struct{} 35 Parameter chan []interface{} 36 } 37 PanicFunc func(err error) 38 ) 39 40 var ( 41 ErrPoolClosed = errors.New("pool has been closed") 42 ErrWaitTimeout = errors.New("pool wait timeout") 43 ) 44 45 // type Options func(*WorkPool) 46 // // func WithReleaseTime 47 // func NewCustom(min int, opt Options) *WorkPool { 48 // w := New(min) 49 // if opt != nil { 50 // opt(w) 51 // } 52 // return w 53 // } 54 55 func New(size int, max ...int) *WorkPool { 56 minIdle := uint(size) 57 if minIdle <= 0 { 58 minIdle = 1 59 } 60 maxIdle := minIdle 61 if len(max) > 0 && max[0] > 0 { 62 m := uint(max[0]) 63 if m > maxIdle { 64 maxIdle = m 65 } 66 } 67 68 w := &WorkPool{ 69 minIdle: minIdle, 70 maxIdle: maxIdle, 71 injector: zdi.New(), 72 queue: make(chan *worker, maxIdle), 73 usedNum: zutil.NewInt64(0), 74 activeNum: zutil.NewInt64(0), 75 releaseTime: time.Second * 60, 76 workers: sync.Pool{New: func() interface{} { 77 return &worker{ 78 jobQueue: make(chan taskfn), 79 Parameter: make(chan []interface{}), 80 stop: make(chan struct{}), 81 } 82 }}, 83 } 84 // todo 定时把队列写入到 chan 85 86 return w 87 } 88 89 // Do Add to the workpool and implement 90 func (wp *WorkPool) Do(fn Task) error { 91 return wp.do(context.Background(), wp.handlerFunc(fn), nil) 92 } 93 94 func (wp *WorkPool) DoWithTimeout(fn Task, t time.Duration) error { 95 ctx, canle := context.WithTimeout(context.Background(), t) 96 defer canle() 97 return wp.do(ctx, wp.handlerFunc(fn), nil) 98 } 99 100 // PanicFunc Do Add to the workpool and implement 101 func (wp *WorkPool) PanicFunc(handler PanicFunc) { 102 wp.panicFunc = handler 103 } 104 105 func (wp *WorkPool) do(cxt context.Context, fn taskfn, param []interface{}) error { 106 if wp.IsClosed() { 107 return ErrPoolClosed 108 } 109 wp.activeNum.Add(1) 110 wp.mu.Lock() 111 run := func(w *worker) { 112 if fn != nil { 113 w.jobQueue <- fn 114 } 115 } 116 add := func() *worker { 117 wp.usedNum.Add(1) 118 wp.mu.Unlock() 119 w := wp.workers.Get().(*worker) 120 go w.createGoroutines(wp, wp.queue, wp.panicFunc) 121 return w 122 } 123 select { 124 case w := <-wp.queue: 125 wp.mu.Unlock() 126 if w != nil { 127 run(w) 128 } else { 129 return ErrPoolClosed 130 } 131 default: 132 switch { 133 case uint(wp.usedNum.Load()) >= wp.minIdle: 134 if uint(wp.usedNum.Load()) < wp.maxIdle { 135 w := add() 136 run(w) 137 return nil 138 } 139 wp.mu.Unlock() 140 select { 141 case <-cxt.Done(): 142 wp.activeNum.Sub(1) 143 return ErrWaitTimeout 144 case w := <-wp.queue: 145 if w != nil { 146 run(w) 147 } else { 148 return ErrPoolClosed 149 } 150 } 151 case uint(wp.usedNum.Load()) < wp.minIdle: 152 w := add() 153 run(w) 154 default: 155 wp.mu.Unlock() 156 } 157 } 158 return nil 159 } 160 161 // IsClosed Has it been closed 162 func (wp *WorkPool) IsClosed() bool { 163 wp.mu.RLock() 164 b := wp.closed 165 wp.mu.RUnlock() 166 return b 167 } 168 169 // Close the pool 170 func (wp *WorkPool) Close() { 171 if wp.IsClosed() { 172 return 173 } 174 wp.mu.Lock() 175 wp.closed = true 176 for 0 < uint(wp.usedNum.Load()) { 177 wp.usedNum.Sub(1) 178 worker := <-wp.queue 179 worker.close() 180 } 181 wp.mu.Unlock() 182 } 183 184 // Wait for the task to finish 185 func (wp *WorkPool) Wait() { 186 for 0 < uint(wp.activeNum.Load()) { 187 time.Sleep(100 * time.Millisecond) 188 } 189 } 190 191 // Pause pause 192 func (wp *WorkPool) Pause() { 193 wp.AdjustSize(0) 194 } 195 196 // Continue to work 197 func (wp *WorkPool) Continue(workerNum ...int) { 198 num := int(wp.maxIdle) 199 if len(workerNum) > 0 { 200 num = workerNum[0] 201 } 202 wp.AdjustSize(num) 203 } 204 205 // Cap get the number of coroutines 206 func (wp *WorkPool) Cap() uint { 207 return uint(wp.usedNum.Load()) 208 } 209 210 // AdjustSize adjust the pool size 211 func (wp *WorkPool) AdjustSize(workSize int) { 212 wp.mu.Lock() 213 defer wp.mu.Unlock() 214 if wp.closed { 215 return 216 } 217 218 oldSize := wp.minIdle 219 newSize := uint(workSize) 220 if newSize > wp.maxIdle { 221 newSize = wp.maxIdle 222 } 223 wp.minIdle = newSize 224 225 if workSize > 0 && oldSize < wp.minIdle { 226 for uint(wp.usedNum.Load()) < wp.minIdle { 227 wp.usedNum.Add(1) 228 w := wp.workers.Get().(*worker) 229 go w.createGoroutines(wp, wp.queue, wp.panicFunc) 230 wp.queue <- w 231 } 232 } 233 for wp.minIdle < uint(wp.usedNum.Load()) { 234 wp.usedNum.Sub(1) 235 worker := <-wp.queue 236 worker.stop <- struct{}{} 237 wp.workers.Put(worker) 238 } 239 } 240 241 func (wp *WorkPool) PreInit() error { 242 if wp.IsClosed() { 243 return ErrPoolClosed 244 } 245 wp.mu.Lock() 246 for uint(wp.usedNum.Load()) < wp.minIdle { 247 wp.usedNum.Add(1) 248 w := wp.workers.Get().(*worker) 249 go w.createGoroutines(wp, wp.queue, wp.panicFunc) 250 wp.queue <- w 251 } 252 wp.mu.Unlock() 253 return nil 254 } 255 256 func (w *worker) createGoroutines(wp *WorkPool, q chan<- *worker, handler PanicFunc) { 257 defer func() { 258 if r := recover(); r != nil { 259 wp.activeNum.Sub(1) 260 err, ok := r.(error) 261 if !ok { 262 err = fmt.Errorf("%v", r) 263 } 264 if err != nil && handler != nil { 265 handler(err) 266 } 267 go w.createGoroutines(wp, q, handler) 268 q <- w 269 } 270 }() 271 if wp.releaseTime > 0 { 272 timer := time.NewTimer(wp.releaseTime) 273 defer timer.Stop() 274 for { 275 select { 276 case job := <-w.jobQueue: 277 timer.Stop() 278 err := job() 279 if err != nil && handler != nil { 280 handler(err) 281 } 282 wp.activeNum.Sub(1) 283 q <- w 284 timer.Reset(wp.releaseTime) 285 // case parameter := <-w.Parameter: 286 // q <- w 287 case <-timer.C: 288 <-wp.queue 289 wp.usedNum.Sub(1) 290 return 291 case <-w.stop: 292 return 293 } 294 } 295 } else { 296 for { 297 select { 298 case job := <-w.jobQueue: 299 err := job() 300 if err != nil && handler != nil { 301 handler(err) 302 } 303 wp.activeNum.Sub(1) 304 q <- w 305 case <-w.stop: 306 return 307 } 308 } 309 } 310 } 311 312 func (w *worker) close() { 313 w.stop <- struct{}{} 314 close(w.stop) 315 close(w.jobQueue) 316 }