MST

星途 面试题库

面试题:Go并发场景下通知退出机制的优化

假设你有多个Go协程并发执行任务,每个协程内部有复杂的业务逻辑和I/O操作。请阐述如何设计一个高效的通知退出机制,确保所有协程能安全、快速地退出,同时避免资源泄漏。请给出详细的设计思路以及关键代码示例。
42.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 信号处理:使用Go的os/signal包来捕获系统信号,如os.Interrupt(通常由用户通过Ctrl+C发送)和syscall.SIGTERM(用于优雅关闭进程的信号)。
  2. 共享退出通道:创建一个全局的退出通道(chan struct{}),所有协程监听这个通道。当收到退出信号时,向该通道发送一个值,通知所有协程退出。
  3. 业务逻辑中的退出检查:在每个协程的业务逻辑中,定期检查退出通道是否有值传入。一旦接收到值,立即停止当前业务逻辑,进行资源清理并退出协程。
  4. 资源清理:在协程退出前,确保所有打开的资源(如文件、数据库连接等)被正确关闭。

关键代码示例

package main

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

func worker(ctx context.Context, id int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Worker %d received exit signal, cleaning up...\n", id)
            // 进行资源清理,如关闭文件、数据库连接等
            return
        default:
            // 模拟复杂业务逻辑和I/O操作
            fmt.Printf("Worker %d is working...\n", id)
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    numWorkers := 3
    var ctx context.Context
    var cancel context.CancelFunc
    ctx, cancel = context.WithCancel(context.Background())

    for i := 0; i < numWorkers; i++ {
        go worker(ctx, i)
    }

    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    go func() {
        sig := <-sigs
        fmt.Println()
        fmt.Println(sig)
        cancel()
    }()

    select {}
}

在上述代码中:

  1. worker函数:模拟了一个协程,它不断检查ctx.Done()通道。当接收到取消信号时,打印清理信息并退出。
  2. main函数
    • 创建了一个可取消的上下文ctx和取消函数cancel
    • 启动多个worker协程。
    • 使用signal.Notify监听系统信号。当接收到SIGINTSIGTERM信号时,调用cancel函数,向所有协程的ctx.Done()通道发送取消信号,从而实现所有协程的安全退出。