什么是对象池
对象池是一种设计模式,它维护一组已经创建好的对象,当需要使用对象时,直接从对象池中获取,使用完毕后再放回对象池,而不是频繁地创建和销毁对象。 这样可以显著减少 gc 的压力,提高程序的性能。
为什么不用 sync.pool
sync.pool 是 go 标准库提供的对象池实现,但它有一些限制:
- gc 不确定性:
sync.pool中的对象可能会被 gc 回收,导致每次获取对象都需要重新创建,失去了对象池的意义。 - 适用场景有限:
sync.pool更适合于临时对象的复用,对于需要长期存在的对象,效果不佳。 - 控制力不足: 无法精确控制对象池的大小和对象的生命周期。
因此,在某些场景下,我们需要自定义对象池,以获得更高的性能和控制力。
手撸对象池:原理与实现
下面,我们就来手撸一个简单的对象池,并分析其原理。
1. 定义对象池结构体
package main
import (
"errors"
"fmt"
"sync"
"time"
)
type pool struct {
objects chan interface{} // 使用 channel 存储对象
factory func() interface{} // 创建对象的工厂函数
mu sync.mutex // 保护对象池
}
var g_index int = 0
func newpool(size int, factory func() interface{}) *pool {
if size <= 0 {
panic("对象池大小必须大于 0")
}
pool := make(chan interface{}, size)
for i := 0; i < size; i++ {
pool <- factory() // 预先创建对象并放入对象池
}
return &pool{
objects: pool,
factory: factory,
}
}
func (p *pool) get() interface{} {
select {
case obj := <-p.objects:
return obj // 从对象池中获取对象
default:
// 对象池为空,创建新对象
fmt.println("create new object")
p.mu.lock()
defer p.mu.unlock()
return p.factory()
}
}
func (p *pool) put(obj interface{}) error {
select {
case p.objects <- obj: // 对象放回对象池
return nil
default:
// 对象池已满,丢弃对象
obj2 := obj.(*myobject)
fmt.println("pool is full, discard object", obj2.index)
obj = nil
return errors.new("pool is full")
}
}
func (p *pool) len() int {
return len(p.objects)
}
type myobject struct {
data string
index int
}
func main() {
// 创建对象工厂
objectfactory := func() interface{} {
g_index += 1
return &myobject{data: "initial data", index: g_index}
}
// 创建对象池,大小为 10
pool := newpool(10, objectfactory)
var wg sync.waitgroup
for i := 0; i < 100; i++ {
wg.add(1)
go func(idx int) {
fmt.println("pool len:", pool.len())
obj := pool.get().(*myobject)
defer func() {
wg.done()
pool.put(obj)
}()
fmt.println("from :", obj.data, obj.index, idx)
time.sleep(time.millisecond * 2) // 模拟一些工作
}(i)
}
wg.wait()
}代码解释:
objectpool结构体包含一个poolchannel,用于存储对象。factory是一个函数,用于创建新的对象。newobjectpool函数用于创建对象池,并预先创建指定数量的对象放入对象池。get函数用于从对象池中获取对象。如果对象池为空,则调用factory创建新的对象。put函数用于将对象放回对象池。如果对象池已满,则丢弃对象。- 定义了一个
myobject结构体,作为对象池中存储的对象类型。 - 创建一个
objectfactory函数,用于创建myobject对象。 - 创建一个大小为 10 的对象池,并传入
objectfactory函数。 - 从对象池中获取对象,修改对象的数据,然后将对象放回对象池。
- 再次从对象池中获取对象,可以看到对象的数据已经被修改,说明对象被成功复用。
总结
通过手撸对象池,我们不仅可以更好地理解对象池的原理,还可以根据实际需求定制对象池,以获得更高的性能和控制力。 在需要频繁创建和销毁对象的场景下,使用对象池可以显著提高程序的性能,告别 gc 噩梦!
到此这篇关于一文详解go语言中对象池的正确打开方式的文章就介绍到这了,更多相关go对象池内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论