github.com/gogf/gf@v1.16.9/os/grpool/grpool.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 // Package grpool implements a goroutine reusable pool. 8 package grpool 9 10 import ( 11 "github.com/gogf/gf/errors/gcode" 12 "github.com/gogf/gf/errors/gerror" 13 14 "github.com/gogf/gf/container/glist" 15 "github.com/gogf/gf/container/gtype" 16 ) 17 18 // Goroutine Pool 19 type Pool struct { 20 limit int // Max goroutine count limit. 21 count *gtype.Int // Current running goroutine count. 22 list *glist.List // Job list for asynchronous job adding purpose. 23 closed *gtype.Bool // Is pool closed or not. 24 } 25 26 // Default goroutine pool. 27 var pool = New() 28 29 // New creates and returns a new goroutine pool object. 30 // The parameter <limit> is used to limit the max goroutine count, 31 // which is not limited in default. 32 func New(limit ...int) *Pool { 33 p := &Pool{ 34 limit: -1, 35 count: gtype.NewInt(), 36 list: glist.New(true), 37 closed: gtype.NewBool(), 38 } 39 if len(limit) > 0 && limit[0] > 0 { 40 p.limit = limit[0] 41 } 42 return p 43 } 44 45 // Add pushes a new job to the pool using default goroutine pool. 46 // The job will be executed asynchronously. 47 func Add(f func()) error { 48 return pool.Add(f) 49 } 50 51 // AddWithRecover pushes a new job to the pool with specified recover function. 52 // The optional <recoverFunc> is called when any panic during executing of <userFunc>. 53 // If <recoverFunc> is not passed or given nil, it ignores the panic from <userFunc>. 54 // The job will be executed asynchronously. 55 func AddWithRecover(userFunc func(), recoverFunc ...func(err error)) error { 56 return pool.AddWithRecover(userFunc, recoverFunc...) 57 } 58 59 // Size returns current goroutine count of default goroutine pool. 60 func Size() int { 61 return pool.Size() 62 } 63 64 // Jobs returns current job count of default goroutine pool. 65 func Jobs() int { 66 return pool.Jobs() 67 } 68 69 // Add pushes a new job to the pool. 70 // The job will be executed asynchronously. 71 func (p *Pool) Add(f func()) error { 72 for p.closed.Val() { 73 return gerror.NewCode(gcode.CodeInvalidOperation, "pool closed") 74 } 75 p.list.PushFront(f) 76 // Check whether fork new goroutine or not. 77 var n int 78 for { 79 n = p.count.Val() 80 if p.limit != -1 && n >= p.limit { 81 // No need fork new goroutine. 82 return nil 83 } 84 if p.count.Cas(n, n+1) { 85 // Use CAS to guarantee atomicity. 86 break 87 } 88 } 89 p.fork() 90 return nil 91 } 92 93 // AddWithRecover pushes a new job to the pool with specified recover function. 94 // The optional <recoverFunc> is called when any panic during executing of <userFunc>. 95 // If <recoverFunc> is not passed or given nil, it ignores the panic from <userFunc>. 96 // The job will be executed asynchronously. 97 func (p *Pool) AddWithRecover(userFunc func(), recoverFunc ...func(err error)) error { 98 return p.Add(func() { 99 defer func() { 100 if exception := recover(); exception != nil { 101 if len(recoverFunc) > 0 && recoverFunc[0] != nil { 102 if err, ok := exception.(error); ok { 103 recoverFunc[0](err) 104 } else { 105 recoverFunc[0](gerror.NewCodef(gcode.CodeInternalError, `%v`, exception)) 106 } 107 } 108 } 109 }() 110 userFunc() 111 }) 112 } 113 114 // Cap returns the capacity of the pool. 115 // This capacity is defined when pool is created. 116 // It returns -1 if there's no limit. 117 func (p *Pool) Cap() int { 118 return p.limit 119 } 120 121 // Size returns current goroutine count of the pool. 122 func (p *Pool) Size() int { 123 return p.count.Val() 124 } 125 126 // Jobs returns current job count of the pool. 127 // Note that, it does not return worker/goroutine count but the job/task count. 128 func (p *Pool) Jobs() int { 129 return p.list.Size() 130 } 131 132 // fork creates a new goroutine worker. 133 // Note that the worker dies if the job function panics. 134 func (p *Pool) fork() { 135 go func() { 136 defer p.count.Add(-1) 137 138 var job interface{} 139 for !p.closed.Val() { 140 if job = p.list.PopBack(); job != nil { 141 job.(func())() 142 } else { 143 return 144 } 145 } 146 }() 147 } 148 149 // IsClosed returns if pool is closed. 150 func (p *Pool) IsClosed() bool { 151 return p.closed.Val() 152 } 153 154 // Close closes the goroutine pool, which makes all goroutines exit. 155 func (p *Pool) Close() { 156 p.closed.Set(true) 157 }