Golang 協程優雅退出

關於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()
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章