Golang 如何优雅的退出程序

在 Golang 中如何在程序中断的时候,等待其他协程执行完毕再退出主程序呢?下面例子演示了使用通道接收中断信号来实现优雅退出的效果。

func main() {
	ch := make(chan os.Signal, 1) //Handle SIGINT and SIGTERM
	log.Println("wait gracefully signal")

	// signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGUSR2)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) //window机子支持的信号量

	sig := <-ch
	log.Println("INFO: Receive signal: " + sig.String())

	time.Sleep(3 * time.Second)

	log.Println("INFO: Server gracefully stopped.")
}

首先创建一个 channel,容量是 1,用来接收信号。

signal.Notify 用来注册要接收的信号,例子中我们注册了 syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGUSR2 这些中断信号,如果符合条件,就会把信号发送到 ch 这个通道。

sig 以阻塞的方式读取 ch 通道,一旦通道有数据,就开始往后面的逻辑执行,休眠几秒钟,这样就可以等待其他正在运行的 goroutine 执行完毕,达到优雅退出的效果。

再看另外一个类似的例子:

func main() {
    // Go signal notification works by sending `os.Signal`
    // values on a channel. We'll create a channel to
    // receive these notifications (we'll also make one to
    // notify us when the program can exit).
    sigs := make(chan os.Signal, 1)
    done := make(chan bool, 1)
    
    // `signal.Notify` registers the given channel to
    // receive notifications of the specified signals.
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
    
    
    // This goroutine executes a blocking receive for
    // signals. When it gets one it'll print it out
    // and then notify the program that it can finish.
    go func() {
        sig := <-sigs
        fmt.Println(sig)
        done <- true
    }()
    
    
    // The program will wait here until it gets the
    // expected signal (as indicated by the goroutine
    // above sending a value on `done`) and then exit.
    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}

这个 demo 有一点不同,另开了一个协程,在协程中接收信号,并且往 done 发送 true,主程序阻塞读取 done 通道再完成退出。