在Go中实现优雅退出通常需要使用context
和信号处理。具体的实现步骤如下:
- 首先,在main函数中创建一个
context
和一个cancel
函数,用于在接收到退出信号时通知goroutines退出。
ctx, cancel := context.WithCancel(context.Background()) defer cancel()
- 接下来,在启动goroutines时,将
context
作为参数传递给它们,并在goroutine中使用select
语句监听context.Done()
和其他通道的消息,以便在接收到退出信号时优雅地退出。
go func(ctx context.Context) { for { select { case <-ctx.Done(): // 收到退出信号,执行清理操作 return case msg := <-msgChan: // 其他业务逻辑 } } }(ctx)
- 最后,在信号处理函数中,使用
context
和cancel
函数通知goroutines退出,并在goroutines退出后退出程序。
c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c // 收到退出信号,通知goroutines退出 cancel() }()
完整的代码实现如下:
package main import ( "context" "fmt" "os" "os/signal" "syscall" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 启动goroutines msgChan := make(chan string) go func(ctx context.Context) { for { select { case <-ctx.Done(): // 收到退出信号,执行清理操作 fmt.Println("goroutine1 exit") return case msg := <-msgChan: // 其他业务逻辑 fmt.Println(msg) } } }(ctx) go func(ctx context.Context) { for { select { case <-ctx.Done(): // 收到退出信号,执行清理操作 fmt.Println("goroutine2 exit") return case msg := <-msgChan: // 其他业务逻辑 fmt.Println(msg) } } }(ctx) // 监听退出信号 c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c // 收到退出信号,通知goroutines退出 cancel() }() // 等待goroutines退出 <-ctx.Done() fmt.Println("all goroutines exit") // 退出程序 os.Exit(0) }
在这个例子中,我们创建了两个goroutines,并使用select
语句在收到退出信号时优雅地退出。当收到退出信号时,会先通知goroutines退出,等待它们退出后再退出程序。