面试题答案
一键面试设计思路
- 资源池:使用一个资源池来管理这些系统资源,避免频繁创建和销毁。资源池可以用一个队列或者链表来实现,存放可用的资源。
- 互斥锁:由于多个Goroutine可能同时访问资源池,需要使用互斥锁(
sync.Mutex
)来保证资源池的线程安全。 - 条件变量:为了在资源池为空时,Goroutine能够等待直到有可用资源,需要使用条件变量(
sync.Cond
)。 - 资源释放:当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()
}