go ants原始碼分析

beginner_z發表於2022-05-06

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
			}
		}
	}()
}

相關文章