關於golang中消費協程優雅退出的寫法,寫了一個簡單的小例子進行記錄一下。
使用場景:
1、生成協程生產數據到管道中
2、消費協程在管道中取數據進行處理
通過捕捉特定信號對程序進行相關處理,當某個信號進行觸發的時候,主協程將向各個協程發送退出指令,當數據管道處理完成時,若接收到退出指令 將結束協程的執行
package main
import (
"fmt"
"git.code.oa.com/gongyi/gongyi_base/log"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
var dataChan chan int
/*************************測試生產類*************************/
type TestProducer struct {
closedChan chan struct{}
wg sync.WaitGroup
}
func (producer *TestProducer)Produce() {
defer producer.wg.Done()
data := 1
for {
dataChan<-data
log.Infof("push data:%d succ", data)
data++
time.Sleep(time.Second * 1)
select {
// 若關閉了通道,直接退出
case <-producer.closedChan:
return
// 不可阻塞
default:
continue
}
}
}
func (producer *TestProducer) Stop() {
close(producer.closedChan)
producer.wg.Wait()
log.Infof("producer has stoped...")
}
/*************************測試消費類*************************/
type TestWorker struct {
workNo int
closedChan chan struct{}
wg sync.WaitGroup
}
func (test* TestWorker)Work() {
defer test.wg.Done()
for {
// 兩個select的目的爲了確保dataChan中的消費完之後才允許退出,因爲單個select中io觸發的順序不確定,可能導致數據管道中還有數據就退出了
select {
case data := <-dataChan:
log.Infof("start to deal data:%d...", data)
time.Sleep(10 * time.Second)
log.Infof("end deal data:%d...", data)
continue
default:
}
select {
case data := <-dataChan:
log.Infof("start to deal data:%d...", data)
time.Sleep(10 * time.Second)
log.Infof("end deal data:%d...", data)
continue
case <-test.closedChan:
log.Infof("worker %d exit...", test.workNo)
return
}
}
}
func (test* TestWorker)Stop() {
close(test.closedChan)
test.wg.Wait()
log.Infof("%d has stoped...", test.workNo)
}
/*************************主邏輯*************************/
func main() {
dataChan = make(chan int, 86400)
// 創建生產協程,並啓動
producer := TestProducer{
closedChan: make(chan struct{}),
}
producer.wg.Add(1)
go producer.Produce()
// 創建消費協程,並啓動
workerNumber := 7
var workers []TestWorker
for i := 0; i < workerNumber; i++ {
workers = append(workers, TestWorker{
workNo: i,
closedChan: make(chan struct{}),
})
}
for i := 0; i < workerNumber; i++ {
workers[i].wg.Add(1)
go workers[i].Work()
}
// 信號處理
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM)
select {
case sig := <-c:
fmt.Printf("Got %s signal. Aborting...\n", sig)
producer.Stop()
for i := 0; i < workerNumber; i++ {
workers[i].Stop()
}
}
}