MST

星途 面试题库

面试题:Go中Goroutine与资源管理

在Go项目中,有一组Goroutine会频繁地创建和销毁一些系统资源(如数据库连接)。请设计一个资源管理机制,确保资源的有效复用与释放,避免资源泄漏,并详细说明你的设计思路和关键代码实现。
47.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 资源池:使用一个资源池来管理这些系统资源,避免频繁创建和销毁。资源池可以用一个队列或者链表来实现,存放可用的资源。
  2. 互斥锁:由于多个Goroutine可能同时访问资源池,需要使用互斥锁(sync.Mutex)来保证资源池的线程安全。
  3. 条件变量:为了在资源池为空时,Goroutine能够等待直到有可用资源,需要使用条件变量(sync.Cond)。
  4. 资源释放:当Goroutine不再使用资源时,需要将资源放回资源池,并且在程序结束时,要确保所有资源都被正确释放。

关键代码实现

package main

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

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

// Resource 代表系统资源,这里以数据库连接为例
type Resource struct {
    DB *sql.DB
}

// ResourcePool 资源池结构体
type ResourcePool struct {
    resources chan *Resource
    size      int
    mu        sync.Mutex
    cond      *sync.Cond
}

// NewResourcePool 创建一个新的资源池
func NewResourcePool(size int) *ResourcePool {
    pool := &ResourcePool{
        resources: make(chan *Resource, size),
        size:      size,
    }
    pool.cond = sync.NewCond(&pool.mu)
    for i := 0; i < size; i++ {
        resource, err := newResource()
        if err != nil {
            panic(err)
        }
        pool.resources <- resource
    }
    return pool
}

// newResource 创建一个新的资源
func newResource() (*Resource, error) {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test")
    if err != nil {
        return nil, err
    }
    return &Resource{DB: db}, nil
}

// GetResource 从资源池获取一个资源
func (p *ResourcePool) GetResource() *Resource {
    p.mu.Lock()
    for len(p.resources) == 0 {
        p.cond.Wait()
    }
    resource := <-p.resources
    p.mu.Unlock()
    return resource
}

// ReturnResource 将资源放回资源池
func (p *ResourcePool) ReturnResource(resource *Resource) {
    p.mu.Lock()
    if len(p.resources) < p.size {
        p.resources <- resource
        p.cond.Broadcast()
    } else {
        // 如果资源池已满,释放资源
        resource.DB.Close()
    }
    p.mu.Unlock()
}

// Close 关闭资源池并释放所有资源
func (p *ResourcePool) Close() {
    p.mu.Lock()
    for len(p.resources) > 0 {
        resource := <-p.resources
        resource.DB.Close()
    }
    close(p.resources)
    p.mu.Unlock()
}

使用示例:

func main() {
    pool := NewResourcePool(5)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            resource := pool.GetResource()
            defer pool.ReturnResource(resource)
            // 使用资源进行数据库操作
            rows, err := resource.DB.Query("SELECT * FROM some_table")
            if err != nil {
                fmt.Println(err)
                return
            }
            defer rows.Close()
            // 处理查询结果
        }()
    }
    wg.Wait()
    pool.Close()
}