golang ants 原始碼分析
結構圖
poolwithfunc與pool相差不大,這裡我們只分析ants預設pool的流程
檔案 | 作用 |
---|---|
ants.go | 定義常量、errors顯示、預設建一個大小為2147483647的goroutine池、封裝一些方便使用者操作檢視goroutine池的函式 |
options.go | goroutine池的相關配置 |
pool.go | 普通pool(不繫結特定函式)的建立以及對pool相關的操作 |
pool_func.go | 建立繫結某個特定函式的pool以及對pool相關的操作 |
worker.go | goworker的struct(其他語言中的類)、run(其他語言中的方法) |
worker_array.go | 一個worker_array的介面和一個能返回實現該介面的函式 |
worker_func.go | 略 |
worker_loop_queue.go | 略 |
worker_stack.go | workerStack(struct)實現worker_array中的所有介面 |
spinlock.go | 鎖相關 |
關鍵結構
type Pool struct
type Pool struct {
capacity int32 // 容量
running int32 // 正在執行的數量
lock sync.Locker //定義一個鎖 用以支援 Pool 的同步操作
workers workerArray // workers 一個介面 存放可迴圈利用的Work(goroutine)的相關資訊
// type workerArray interface {
// len() int
// isEmpty() bool
// insert(worker *goWorker) error
// detach() *goWorker
// retrieveExpiry(duration time.Duration) []*goWorker
// reset()
// }
state int32 //記錄池子的狀態(關閉,開啟)
cond *sync.Cond // 條件變數
workerCache sync.Pool // golang原始池子 使用sync.Pool物件池管理和建立worker物件,提升效能
blockingNum int // 阻塞等待的任務數量;
stopHeartbeat chan struct{} //一個空結構體的通道,僅用於接收標誌
options *Options // 用於配置pool的options指標
}
- func (p *Pool) purgePeriodically() //定期清理過期worker任務
- func (p *Pool) Submit(task func()) error //提交func任務與worker繫結進行執行
- func (p *Pool) Running() int //有多少個執行的worker
- func (p *Pool) Free() int //返回空閒的worker數量
- func (p *Pool) Cap() int // 返回pool的容量
- ......
- func (p *Pool) retrieveWorker() (w *goWorker) //返回一個worker
workerArray
type workerArray interface {
len() int // worker的數量
isEmpty() bool // worker是否為0
insert(worker *goWorker) error //將執行完的worker(goroutine)放回
detach() *goWorker // 獲取worker
retrieveExpiry(duration time.Duration) []*goWorker //取出所有的過期 worker;
reset() // 重置
}
workerStack
type workerStack struct {
items []*goWorker //空閒的worker
expiry []*goWorker //過期的worker
size int
}
下面是對介面workerArray的實現
func (wq *workerStack) len() int {
return len(wq.items)
}
func (wq *workerStack) isEmpty() bool {
return len(wq.items) == 0
}
func (wq *workerStack) insert(worker *goWorker) error {
wq.items = append(wq.items, worker)
return nil
}
//返回items中最後一個worker
func (wq *workerStack) detach() *goWorker {
l := wq.len()
if l == 0 {
return nil
}
w := wq.items[l-1]
wq.items[l-1] = nil // avoid memory leaks
wq.items = wq.items[:l-1]
return w
}
func (wq *workerStack) retrieveExpiry(duration time.Duration) []*goWorker {
n := wq.len()
if n == 0 {
return nil
}
expiryTime := time.Now().Add(-duration) //過期時間=現在的時間-1s
index := wq.binarySearch(0, n-1, expiryTime)
wq.expiry = wq.expiry[:0]
if index != -1 {
wq.expiry = append(wq.expiry, wq.items[:index+1]...) //因為以後進先出的模式去worker 所有過期的woker這樣wq.items[:index+1]取
m := copy(wq.items, wq.items[index+1:])
for i := m; i < n; i++ { //m是存活的數量 下標為m之後的元素全部置為nil
wq.items[i] = nil
}
wq.items = wq.items[:m] //抹除後面多餘的內容
}
return wq.expiry
}
// 二分法查詢過期的worker
func (wq *workerStack) binarySearch(l, r int, expiryTime time.Time) int {
var mid int
for l <= r {
mid = (l + r) / 2
if expiryTime.Before(wq.items[mid].recycleTime) {
r = mid - 1
} else {
l = mid + 1
}
}
return r
}
func (wq *workerStack) reset() {
for i := 0; i < wq.len(); i++ {
wq.items[i].task <- nil //worker的任務置為nil
wq.items[i] = nil //worker置為nil
}
wq.items = wq.items[:0] //items置0
}
流程分析
建立pool
func NewPool(size int, options ...Option) (*Pool, error) {
opts := loadOptions(options...) // 匯入配置
根據不同項進行配置此處省略
p := &Pool{
capacity: int32(size),
lock: internal.NewSpinLock(),
stopHeartbeat: make(chan struct{}, 1), //開一個通道用於接收一個停止標誌
options: opts,
}
p.workerCache.New = func() interface{} {
return &goWorker{
pool: p,
task: make(chan func(), workerChanCap),
}
}
p.workers = newWorkerArray(stackType, 0)
p.cond = sync.NewCond(p.lock)
go p.purgePeriodically()
return p, nil
}
提交任務(將worker於func繫結)
func (p *Pool) retrieveWorker() (w *goWorker) {
spawnWorker := func() {
w = p.workerCache.Get().(*goWorker)
w.run()
}
p.lock.Lock()
w = p.workers.detach() // 獲取列表中最後一個worker
if w != nil { // 取出來的話直接解鎖
p.lock.Unlock()
} else if capacity := p.Cap(); capacity == -1 || capacity > p.Running() { //沒取到但是容量為無限大或者容量未滿
p.lock.Unlock()
spawnWorker() //開一個新的worker
} else { // 沒取到 而且容量已經滿了
if p.options.Nonblocking { //預設為False
p.lock.Unlock()
return
}
retry:
xxxx
goto retry
xxxx
p.lock.Unlock()
}
return
}
goworker的執行
func (w *goWorker) run() {
w.pool.incRunning() //增加正在執行的worker數量
go func() {
defer func() {
w.pool.decRunning()
w.pool.workerCache.Put(w)
if p := recover(); p != nil {
if ph := w.pool.options.PanicHandler; ph != nil {
ph(p)
} else {
w.pool.options.Logger.Printf("worker exits from a panic: %v\n", p)
var buf [4096]byte
n := runtime.Stack(buf[:], false)
w.pool.options.Logger.Printf("worker exits from panic: %s\n", string(buf[:n]))
}
}
// Call Signal() here in case there are goroutines waiting for available workers.
w.pool.cond.Signal()
}()
for f := range w.task { //阻塞接受task
if f == nil {
return
}
f() //執行函式
if ok := w.pool.revertWorker(w); !ok { // 將goworker放回items中
return
}
}
}()
}