又是一個golang func的簡單運用,不得不說這個語法糖真的很棒。
併發任務消費的本質其實就是多個自旋的goroutine來監聽多個func 型別的chan,當收到從chan傳遞過來的func後,就執行之。
核心程式碼
type Work func()// 重新定義func的型別
var workChan []chan Work// 接收工作的chan
var wg = &sync.WaitGroup{}
func Start(size int) {// size規定了自旋的goroutine的個數
for i := 0; i < size; i++ {
workChan[i] = make(chan Work)
go process(i)
}
}
func process(i int) {
wg.Add(1)
defer wg.Done()
if works, ok := workChan[i]; ok {
for {
work, ok := <-works// 收到工作
if ok {
work()// 執行工作
}
}
}
}
複製程式碼
通過定義一個slice型別的chan Work,使用多個goroutine來接收chan的工作,收到工作便執行。同時使用sync.WaitGroup來保證goroutine的無限自旋。
新增工作
外部傳送工作其實就是往chan內傳送工作的邏輯,考慮到為外部提供更為良好的使用,於是簡單封裝一下,如下
type Work func()
type workerGroup struct {
workChan map[int]chan Work
*sync.WaitGroup
isStop bool
}
func NewWorkerGroup() *workerGroup {
wg := &workerGroup{WaitGroup: &sync.WaitGroup{}}
wg.workChan = make(map[int]chan Work)
return wg
}
// 傳送任務
func (w *workerGroup) SendWork(work Work, i int) error {
if !w.isStop {
if works, ok := w.workChan[i]; ok {
works <- work
return nil
} else {
return errors.New("error i")
}
}else{
return errors.New("the worker group has been closed")
}
}
func (w *workerGroup) process(i int) {
w.Add(1)
defer w.Done()
if works, ok := w.workChan[i]; ok {
for {
work, ok := <-works
if ok {
work()
}
}
}
}
func (w *workerGroup) Start(size int) {
for i := 0; i < size; i++ {
w.workChan[i] = make(chan Work)
go w.process(i)
}
}
// 監聽退出
func (w *workerGroup) OnStop() {
go func() {
for {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
select {
case <-sig:
w.isStop = true
func() {
for _, works := range w.workChan {
if len(works) <=0 {
close(works)
}
}
}()
}
break
}
log.Println("worker group succeed to close")
os.Exit(1)
}()
w.Wait()
}
複製程式碼
外部程式碼需要使用到這個模組的時候,只需要建立工作組,開啟一下,呼叫sendwork來傳送工作即可,如下
func main() {
wg := NewWorkerGroup()
wg.Start(6)
tick := time.Tick(time.Second)
wg.OnStop()
wg.SendWork(func() {
fmt.Println("work")
}, 5)
}
複製程式碼
總結
以上程式碼只是簡單的使用,並沒有考慮很多的異常情況,比如在SendWork函式中,若是chan裡面已經存在值了,那麼sendwork就會被阻塞;其次,若是系統退出時,goroutine中存在未完成的工作,也會一併退出;此外,若是工作具有返回值,需要通知結果,是直接逐層返回結果還是用非同步通知回撥函式的方式,都有待考量。還有更多的問題,歡迎拍磚指正。