Go 如何优雅退出进程

发布于:2025-02-11 ⋅ 阅读:(82) ⋅ 点赞:(0)

优雅退出设计步骤

在 Go 项目中,设计优雅退出(Graceful Shutdown)时,通常需要确保在收到退出信号时,程序能够安全地清理资源并优雅地退出。以下是常见的优雅退出设计步骤:

步骤 1:创建 context.Contextcancel 函数

  • 使用 context.WithCancel() 创建一个取消上下文,用于传递取消信号。
  • cancel() 函数可以在收到退出信号时调用,从而通知所有正在运行的 goroutine 停止执行。

步骤 2:通过 context.WithCancel() 创建取消上下文

  • 为确保优雅退出,cancel() 函数需要在主线程的退出信号处理之前执行,保证所有协程能够及时响应取消信号。

步骤 3:处理所有协程的退出

  • 启动多个协程并确保它们能够响应 ctx.Done() 信号,及时停止执行并清理资源。
  • 使用 select 语句监听 ctx.Done(),以便协程能够在收到取消信号时退出。

步骤 4:触发 cancel() 并等待协程退出

  • 在收到退出信号(如系统信号 SIGINT, SIGTERM 等)时,优先调用 cancel(),以确保通知所有正在运行的协程退出。
  • 然后等待所有协程退出,通常使用 sync.WaitGroup 来等待所有协程完成退出。
package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

func doWork(ctx context.Context, id int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			// 当收到取消信号时,退出 goroutine
			fmt.Printf("goroutine %d: work cancelled\n", id)
			return
		default:
			// 模拟工作
			fmt.Printf("goroutine %d: working...\n", id)
			time.Sleep(1 * time.Second)
		}
	}
}

func main() {
	// 创建一个取消上下文
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	var wg sync.WaitGroup
	// 启动多个 goroutine
	for i := 0; i < 3; i++ {
		wg.Add(1)
		go doWork(ctx, i, &wg)
	}

	// 捕获终止信号
	signalChan := make(chan os.Signal, 1)
	// syscall.SIGINT: 终端执行 Ctrl + C
	// syscall.SIGTERM: 终端执行 kill $pid
	// syscall.SIGQUIT: 终端执行 Ctrl + \
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	// 等待退出信号
	select {
	case <-signalChan:
		// 在退出信号到达时,先执行 cancel,发出取消信号
		fmt.Println("Received termination signal. Cancelling context...")
		cancel()

		// 等待所有 goroutine 完成退出
		wg.Wait()
		fmt.Println("Graceful shutdown completed.")
	}
}


网站公告

今日签到

点亮在社区的每一天
去签到