设计思路
- 原子操作:使用
atomic
包中的函数来对共享资源进行原子性的读写操作,这样可以避免数据竞争。例如,对于整数类型的共享资源,可以使用atomic.AddInt64
、atomic.LoadInt64
等函数。
- Channel:通过channel来协调Goroutine之间的通信和同步。可以将对共享资源的操作封装成消息,通过channel发送给专门处理这些操作的Goroutine,这样可以保证操作的顺序性,进一步避免数据竞争。
- 资源分配与回收:
- 分配:可以在初始化时预先分配一定数量的资源,并将这些资源的标识通过channel发送给需要使用资源的Goroutine。
- 回收:当Goroutine使用完资源后,通过另一个channel将资源标识发送回资源管理Goroutine,以便再次分配。
代码实现
package main
import (
"fmt"
"sync"
"sync/atomic"
)
// 共享资源结构体
type Resource struct {
id int64
// 其他资源相关字段
}
// 资源池
type ResourcePool struct {
resources chan *Resource
counter int64
}
// 创建资源池
func NewResourcePool(size int) *ResourcePool {
pool := &ResourcePool{
resources: make(chan *Resource, size),
counter: 0,
}
for i := 0; i < size; i++ {
pool.resources <- &Resource{id: atomic.AddInt64(&pool.counter, 1)}
}
return pool
}
// 获取资源
func (p *ResourcePool) GetResource() *Resource {
return <-p.resources
}
// 释放资源
func (p *ResourcePool) ReleaseResource(r *Resource) {
p.resources <- r
}
func main() {
var wg sync.WaitGroup
pool := NewResourcePool(5)
// 模拟多个Goroutine操作资源
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
resource := pool.GetResource()
defer pool.ReleaseResource(resource)
// 对资源进行操作
fmt.Printf("Goroutine using resource %d\n", resource.id)
}()
}
wg.Wait()
}
资源分配与回收说明
- 资源分配:
NewResourcePool
函数在初始化时,创建了一定数量的Resource
实例,并将它们放入resources
channel中。GetResource
函数从这个channel中获取资源,实现资源的分配。
- 资源回收:
ReleaseResource
函数将使用完的资源重新放回resources
channel中,以便其他Goroutine再次获取,实现资源的回收。通过这种方式,确保了资源的稳定分配与回收,提高了系统的高效性。同时,使用原子操作和channel避免了数据竞争,保证了系统在高并发场景下的稳定性。