Go簡單的結束程序

Go簡單的結束程序

前請概要

當你寫的Go程序在退出時做一些操作就需要捕捉信號後進行業務處理再關閉程序
但是當程序主線不止一條時,邏輯會些許複雜。一般捕捉信號的函數只有一個,但是退出的地方卻多了起來,退出方案必須具有拓展性與簡單性才能讓自己不再糾結於此。
一般資料都時在講捕捉信號與退出,而例子往往只考慮單個主線退出,當你寫個命令行啓動,根據子命令執行不同主線時在方便的擴展性上迷了方向。

舉個栗子

單條主線

func main() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, os.Kill)
 	// TODO: do something
    s := <-c
    fmt.Println("Got signal:", s)
}

全在main裏幹了,耦合性太強,業務需在main中按邏輯調用,使用讀寫無緩衝區通道會阻塞的特點

多條主線

我有個命令行程序,一個子命令啓動server,一個子命令啓動client,兩個命令啓動的程序都需要清理退出。

  • 暫且把信號通道作爲全局變量放在global.go
var ExitChan =make(chan string)
var SigChan = make(chan os.Signal)
var Wt=sync.WaitGroup{}
  • main.go只處理信號監聽與cmd解析主入口,然後阻塞等待協程。這裏把信號轉換到字符串通道是爲了兼容其他錯誤退出的需求
func exitListen() {
	signal.Notify(utils.SigChan, syscall.SIGINT, syscall.SIGTERM)
	utils.ExitChan <- fmt.Sprintf("%v", <-utils.SigChan)
}

func main() {
	go exitListen()
	go cmd.Execute()
	utils.Wt.Add(1)
	utils.Wt.Wait()
}
  • server.go 模板方式初見端倪,只需在業務之上做一個信號退出處理,然後將主協程阻塞Done一下,整個程序就結束了,客戶端照葫蘆畫瓢,不需要侵入業務進行管理協程。
var serverCmd = &cobra.Command{
	Use:   "server",
	Aliases: []string{"s","service"},
	Short: "啓動服務",
	Long: ``,
	Run: func(cmd *cobra.Command, args []string) {
		go func() {
			logrus.Warnf("服務中止,退出!%v", <-utils.ExitChan)
			communication.Offline()
			utils.Wt.Done()
		}()
		communication.UdpListen()
		communication.Online()
		server:=communication.NewMessageServer()
		server.Listen()
	},
}

結語

此方案適用於不需要複雜精細控制子協程的場合,相較於只阻塞監聽信號,然後處理退出的思想來說多了多主線的兼容性,且簡單的做到多信號搶佔,只要一個觸發可以退出所有。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章