MST

星途 面试题库

面试题:Go并发服务关闭时的资源清理与竞争处理

假设你正在开发一个基于Go的并发服务,该服务在运行过程中会创建数据库连接、文件句柄等资源。当需要优雅关闭服务时,如何确保这些资源被正确清理,同时避免在关闭过程中出现资源竞争问题?请详细描述你的解决方案,并给出相关代码片段。
20.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

解决方案

  1. 使用context.Context:Go语言中的context.Context用于在不同的goroutine之间传递截止时间、取消信号等信息。我们可以使用context.Context来通知各个goroutine需要关闭服务。
  2. 同步资源清理:使用sync.WaitGroup来等待所有资源清理完成,确保没有资源竞争。
  3. 数据库连接与文件句柄管理:对于数据库连接,可以使用数据库驱动提供的关闭方法;对于文件句柄,使用os.FileClose方法。

代码示例

package main

import (
    "context"
    "fmt"
    "os"
    "sync"
    "time"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

var (
    db  *gorm.DB
    ctx context.Context
    wg  sync.WaitGroup
)

func initDB() error {
    var err error
    dsn := "user:password@tcp(127.0.0.1:3306)/your_database?charset=utf8mb4&parseTime=True&loc=Local"
    db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        return err
    }
    return nil
}

func main() {
    // 初始化数据库连接
    if err := initDB(); err != nil {
        fmt.Println("Failed to initialize database:", err)
        return
    }
    defer func() {
        if db != nil {
            sqlDB, _ := db.DB()
            sqlDB.Close()
        }
    }()

    // 创建上下文
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 启动一个goroutine模拟服务
    wg.Add(1)
    go func() {
        defer wg.Done()
        for {
            select {
            case <-ctx.Done():
                fmt.Println("Service is shutting down...")
                return
            default:
                // 模拟服务逻辑
                fmt.Println("Service is running...")
                time.Sleep(1 * time.Second)
            }
        }
    }()

    // 模拟文件操作
    file, err := os.OpenFile("test.txt", os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        fmt.Println("Failed to open file:", err)
        return
    }
    defer file.Close()

    // 模拟运行一段时间后关闭服务
    time.Sleep(5 * time.Second)
    fmt.Println("Initiating graceful shutdown...")
    cancel()

    // 等待所有资源清理完成
    wg.Wait()
    fmt.Println("Service has been gracefully shut down.")
}

在上述代码中:

  1. context.Context用于通知goroutine关闭服务。
  2. sync.WaitGroup等待服务goroutine完成资源清理。
  3. 数据库连接使用gorm库,并在服务结束时关闭连接。
  4. 文件句柄使用os.OpenFile打开,并通过defer确保在函数结束时关闭。