MST

星途 面试题库

面试题:Go匿名函数与闭包在并发场景下的深入应用

在一个并发的Go程序中,需要使用匿名函数和闭包实现一个资源池。资源池需要支持动态分配和回收资源,并且在多个goroutine并发访问时保证数据的一致性和安全性。请详细设计并实现这个资源池,包括必要的同步机制和错误处理。
22.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
    "sync"
)

// 定义资源类型
type Resource struct {
    // 资源的具体数据
    Data string
}

// 定义资源池
type ResourcePool struct {
    resources chan *Resource
    max       int
    mu        sync.Mutex
    inUse     map[*Resource]bool
}

// 创建资源池
func NewResourcePool(max int) *ResourcePool {
    return &ResourcePool{
        resources: make(chan *Resource, max),
        max:       max,
        inUse:     make(map[*Resource]bool),
    }
}

// 从资源池获取资源
func (rp *ResourcePool) Get() (*Resource, error) {
    rp.mu.Lock()
    if len(rp.resources) == 0 && len(rp.inUse) >= rp.max {
        rp.mu.Unlock()
        return nil, fmt.Errorf("资源池已耗尽")
    }
    rp.mu.Unlock()

    select {
    case res := <-rp.resources:
        rp.mu.Lock()
        rp.inUse[res] = true
        rp.mu.Unlock()
        return res, nil
    default:
        // 创建新资源
        newRes := &Resource{Data: "新资源"}
        rp.mu.Lock()
        rp.inUse[newRes] = true
        rp.mu.Unlock()
        return newRes, nil
    }
}

// 归还资源到资源池
func (rp *ResourcePool) Put(res *Resource) {
    rp.mu.Lock()
    if _, ok := rp.inUse[res]; ok {
        delete(rp.inUse, res)
        select {
        case rp.resources <- res:
        default:
            // 资源池已满,直接丢弃
        }
    }
    rp.mu.Unlock()
}

func main() {
    pool := NewResourcePool(5)

    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            res, err := pool.Get()
            if err != nil {
                fmt.Printf("goroutine %d 获取资源失败: %v\n", id, err)
                return
            }
            defer pool.Put(res)
            fmt.Printf("goroutine %d 使用资源: %v\n", id, res.Data)
        }(i)
    }

    wg.Wait()
}

上述代码实现了一个资源池:

  1. Resource 结构体:定义了资源的类型。
  2. ResourcePool 结构体:包含资源通道 resources、最大资源数 max、互斥锁 mu 和正在使用的资源映射 inUse
  3. NewResourcePool 函数:用于创建资源池实例。
  4. Get 方法:从资源池获取资源,如果资源池为空且使用中的资源达到最大数,则返回错误。否则从通道获取资源或创建新资源。
  5. Put 方法:将资源归还到资源池,如果资源正在使用,则从 inUse 中删除并尝试放入通道,如果通道已满则丢弃。
  6. main 函数:演示了如何使用资源池,启动多个 goroutine 并发获取和归还资源。