在Go中实现优雅退出

42 min read

在Go中实现优雅退出通常需要使用context和信号处理。具体的实现步骤如下:

  1. 首先,在main函数中创建一个context和一个cancel函数,用于在接收到退出信号时通知goroutines退出。
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
  1. 接下来,在启动goroutines时,将context作为参数传递给它们,并在goroutine中使用select语句监听context.Done()和其他通道的消息,以便在接收到退出信号时优雅地退出。
go func(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            // 收到退出信号,执行清理操作
            return
        case msg := <-msgChan:
            // 其他业务逻辑
        }
    }
}(ctx)
  1. 最后,在信号处理函数中,使用contextcancel函数通知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退出,等待它们退出后再退出程序。