MST

星途 面试题库

面试题:Go闭包在复杂数据结构和资源管理中的潜在错误及规避策略

假设你正在开发一个Go程序,需要管理数据库连接池。你使用闭包来封装获取和释放数据库连接的逻辑,如下代码: ```go package main import ( "database/sql" "fmt" "log" _ "github.com/go - sql - driver/mysql" ) func newDBConnectionPool() func() (*sql.DB, error) { db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test") if err != nil { log.Fatal(err) } return func() (*sql.DB, error) { return db, nil } } func main() { getDB := newDBConnectionPool() db, err := getDB() if err != nil { log.Fatal(err) } defer db.Close() // 执行数据库操作 rows, err := db.Query("SELECT * FROM users") if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { // 处理结果 } } ``` 这段代码在使用闭包管理数据库连接池时存在什么潜在错误?如何改进代码以正确地处理连接池的创建、获取、使用和释放,同时避免资源泄漏和竞争条件等问题?请详细阐述闭包在这个过程中的作用以及可能引入的错误场景。
14.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

潜在错误

  1. 连接池管理缺失:代码只是简单地创建了一个数据库连接,并没有实现真正的连接池管理。没有复用连接,每次调用 getDB 实际没有从连接池中获取连接。
  2. 未处理连接关闭:虽然在 main 函数中有 defer db.Close(),但如果 getDB 被多次调用,其他地方没有合适的关闭逻辑,可能导致连接未正确关闭,造成资源泄漏。
  3. 竞争条件:代码没有考虑到多个 goroutine 同时访问数据库连接的情况,可能会引发竞争条件。

改进代码

package main

import (
    "database/sql"
    "fmt"
    "log"
    "sync"

    _ "github.com/go - sql - driver/mysql"
)

var (
    once     sync.Once
    db       *sql.DB
    err      error
    poolSize = 10
)

func newDBConnectionPool() func() (*sql.DB, error) {
    var pool sync.Pool
    once.Do(func() {
        db, err = sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test")
        if err != nil {
            log.Fatal(err)
        }
        db.SetMaxIdleConns(poolSize)
        db.SetMaxOpenConns(poolSize)
    })

    pool.New = func() interface{} {
        conn, err := db.Conn(nil)
        if err != nil {
            log.Fatal(err)
        }
        return conn
    }

    return func() (*sql.DB, error) {
        conn := pool.Get().(*sql.Conn)
        defer func() {
            pool.Put(conn)
        }()
        return db, nil
    }
}

func main() {
    getDB := newDBConnectionPool()
    db, err := getDB()
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err := db.Close(); err != nil {
            log.Println(err)
        }
    }()
    // 执行数据库操作
    rows, err := db.Query("SELECT * FROM users")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    for rows.Next() {
        // 处理结果
    }
}

闭包的作用

闭包在这个过程中封装了获取和释放数据库连接的逻辑,使得代码结构更清晰。通过闭包可以隐藏连接池的具体实现细节,外部调用者只需要调用闭包函数获取连接,而不需要关心连接池内部是如何创建、管理和复用连接的。

可能引入的错误场景

  1. 资源泄漏:如果在闭包内部没有正确处理连接的释放,例如忘记在使用完连接后放回连接池,就会导致连接一直被占用,最终耗尽资源。
  2. 竞争条件:当多个 goroutine 同时调用闭包获取连接时,如果没有适当的同步机制,可能会导致连接池状态不一致,例如同时获取到同一个连接,或者连接被错误地重复释放等问题。