創建一個協成複用,限制協成數量的協成池
package pool
import (
"fmt"
)
type Pool interface {
Schedule(task func()) error
}
type pool struct {
work chan func()
sem chan struct{} // 計數,限制協成數
}
func New(size int) Pool {
return &pool{
work: make(chan func()),
sem: make(chan struct{}, size),
}
}
func (p *pool) Schedule(task func()) error {
select {
case p.work <- task:
case p.sem <- struct{}{}:
go p.worker(task)
}
return nil
}
func (p *pool) worker(task func()) {
defer func() { <-p.sem }()
for {
task()
task = <-p.work
}
}
1. p.work時無緩衝,所以第一次調用Schedule時case p.work <- task: 由於沒有協成讀work,就不會寫入,就爲false, 2. p.sem因爲有緩衝區,不需要有讀協成讀就可以寫入,所以會執行case p.sem <- struct{}{},往sem寫入一個空對象,接着通過go p.workder(task)開啓一個協成處理task。 3. task處理結束後,協成不銷燬,會停在task = <-p.work等待接收p.work裏的任務 2. 第二次調用Schedule,如果第一次創建的協成等待接收新的task(task = <-p.work),就會執行case p.work <- task,往work的channel寫數據,協成就能從p.work取到(task = <-p.work)task執行,不會開啓新的協成。 3. 如果task執行時間比較久,第二次調用Schedule時,task()還未執行完,也就是還未執行到task = <-p.work,那麼case p.work <- task將無法寫入,當p.sem緩衝區沒滿(還未達到限制size)就會執行case p.sem <- struct{}{},開啓新的協成執行task. 4. 當p.sem緩衝區滿了,也就是說達到限制的最大數時,Schedule將會pending,直到某個協成執行到task = <-p.work接收任務 5. defer func() { <-p.sem }()是用在回收,在一些情況下,希望協成銷燬,就可以跳出for循環,sem讀出數據,也就是線程數數-1