github.com/nyan233/littlerpc@v0.4.6-0.20230316182519-0c8d5c48abaf/internal/pool/task_pool.go (about) 1 package pool 2 3 import ( 4 "context" 5 "errors" 6 "sync" 7 "sync/atomic" 8 "time" 9 ) 10 11 const ( 12 MaxTaskPoolSize = 1024 * 16 13 ) 14 15 type RecoverFunc func(poolId int, err interface{}) 16 17 // DynamicTaskPool 18 // v0.10 -> v0.36 实现了简单的任务池 19 // v0.38 -> now 实现了可自动扩容的Goroutine池和可拓展的接口 20 type DynamicTaskPool[Key Hash] struct { 21 // buf chan 22 tasks chan func() 23 recoverFn RecoverFunc 24 // 用于取消所有goroutine 25 ctx context.Context 26 cancelFn context.CancelFunc 27 // 统计关闭的goroutine数量 28 wg *sync.WaitGroup 29 // 现在活跃的goroutine数量 30 liveSize int32 31 // 池的大小,也即活跃的goroutine数量 32 minSize int32 33 // 最大的池大小 34 maxSize int32 35 idleTicker *time.Ticker 36 // 关闭的标志 37 closed int32 38 // 执行成功的统计 39 _ [128 - 88]byte 40 execSuccess uint64 41 _ [128 - 8]byte 42 // 执行失败的统计 43 execFailed uint64 44 } 45 46 func NewTaskPool[Key Hash](bufSize, minSize, maxSize int32, rf RecoverFunc) TaskPool[Key] { 47 pool := new(DynamicTaskPool[Key]) 48 if bufSize > MaxTaskPoolSize { 49 bufSize = MaxTaskPoolSize 50 } 51 pool.tasks = make(chan func(), bufSize) 52 pool.recoverFn = rf 53 pool.minSize = minSize 54 pool.maxSize = maxSize 55 pool.idleTicker = time.NewTicker(time.Second * 90) 56 pool.wg = &sync.WaitGroup{} 57 pool.wg.Add(int(minSize)) 58 pool.ctx, pool.cancelFn = context.WithCancel(context.Background()) 59 pool.start() 60 return pool 61 } 62 63 func (p *DynamicTaskPool[Key]) start() { 64 for i := 0; i < int(p.minSize); i++ { 65 gIndex := atomic.AddInt32(&p.liveSize, 1) 66 go func() { 67 exec[struct{}, any, Key](p, gIndex, p.ctx.Done(), nil) 68 }() 69 } 70 } 71 72 func exec[T any, T2 any, Key Hash](p *DynamicTaskPool[Key], gIndex int32, done <-chan T, done2 <-chan T2) { 73 defer p.wg.Done() 74 defer atomic.AddInt32(&p.liveSize, -1) 75 iFunc := func(fn func()) { 76 defer func() { 77 if err := recover(); err != nil { 78 atomic.AddUint64(&p.execFailed, 1) 79 p.recoverFn(int(gIndex), err) 80 } else { 81 atomic.AddUint64(&p.execSuccess, 1) 82 } 83 }() 84 fn() 85 } 86 for { 87 select { 88 case fn := <-p.tasks: 89 iFunc(fn) 90 case <-done: 91 return 92 case <-done2: 93 // select一个为nil的chan将始终阻塞, 所以done2在start()函数调用时必须是 == nil 94 return 95 } 96 } 97 } 98 99 func (p *DynamicTaskPool[Key]) Push(key Key, fn func()) error { 100 if atomic.LoadInt32(&p.closed) == 1 { 101 return errors.New("pool already closed") 102 } 103 select { 104 case p.tasks <- fn: 105 break 106 default: 107 // 阻塞表示buf已满, 需要扩容Goroutine 108 for { 109 oldLiveSize := atomic.LoadInt32(&p.liveSize) 110 if oldLiveSize >= p.maxSize { 111 // 已到达Goroutine上限则不扩容, 等待可用 112 p.tasks <- fn 113 return nil 114 } 115 if atomic.CompareAndSwapInt32(&p.liveSize, oldLiveSize, oldLiveSize+1) { 116 p.wg.Add(1) 117 go func() { 118 p.idleTicker.Reset(time.Second * 90) 119 exec[time.Time, struct{}](p, oldLiveSize+1, p.idleTicker.C, p.ctx.Done()) 120 }() 121 p.tasks <- fn 122 return nil 123 } else { 124 // retry 125 continue 126 } 127 } 128 } 129 return nil 130 } 131 132 func (p *DynamicTaskPool[Key]) Stop() error { 133 if !atomic.CompareAndSwapInt32(&p.closed, 0, 1) { 134 return errors.New("pool already closed") 135 } 136 p.cancelFn() 137 p.wg.Wait() 138 return nil 139 } 140 141 func (p *DynamicTaskPool[Key]) LiveSize() int { 142 return int(atomic.LoadInt32(&p.liveSize)) 143 } 144 145 func (p *DynamicTaskPool[Key]) BufSize() int { 146 return len(p.tasks) 147 } 148 149 func (p *DynamicTaskPool[Key]) ExecuteSuccess() int { 150 return int(atomic.LoadUint64(&p.execSuccess)) 151 } 152 153 func (p *DynamicTaskPool[Key]) ExecuteError() int { 154 return int(atomic.LoadUint64(&p.execFailed)) 155 }