对象池在大家平时的工作中也是经常遇见的,尤其是创建代价笔记高的对象,比如数据库的链接,经常会用到对象池,避免重复创建。
在go语言中,可以使用buffer channel实现一个对象池。
初始化对象池
首先,预设一个对象池的数据结构
type ResourcePool struct {
}
type ObjPool struct {
bufChan chan *ResourcePool
}
然后初始化对象池里面的对象,用来使用。 初始化一个buffer channel,通过外部的配置,用来控制buffer的长度,然后把对应的空对象写入channel里面。
func NewObjPool(numObj int) *ObjPool {
objPool := ObjPool{}
objPool.bufChan = make(chan *ResourcePool, numObj)
for i := 0; i < numObj; i++ {
objPool.bufChan <- &ResourcePool{}
}
return &objPool
}
从对象池获取对象
对象池初始化完成之后,外部需要在对象池,获取对象。
使用select,用来用来消费buffer channel里面的对象。
func (p *ObjPool) GetObj(timeout time.Duration) (*ResourcePool, error) {
select {
case ret := <-p.bufChan:
return ret, nil
case <-time.After(timeout):
return nil, errors.New("timeout")
}
}
释放对象
在对应的对象使用完成之后,我们需要再把对象释放掉,重新写入到对象池中
func (p *ObjPool) ReleaseObj(obj *ResourcePool) error {
select {
case p.bufChan <- obj:
return nil
default:
return errors.New("overflow")
}
}
测试函数
我们简单写个测试函数,进行一个测试
func TestObjPool(t *testing.T) {
pool := NewObjPool(10)
for i := 0; i < 11; i++ {
if v, err := pool.GetObj(time.Second * 1); err != nil {
t.Error(err)
} else {
t.Logf("get obj: %p", v)
if err := pool.ReleaseObj(v); err != nil {
t.Error(err)
}
}
}
fmt.Println("Done")
}
输出
=== RUN TestObjPool
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
obj_pool_test.go:57: get obj: 0xa637e8
Done
--- PASS: TestObjPool (0.00s)
PASS
完整代码
package Class33
import (
"errors"
"fmt"
"testing"
"time"
)
type ResourcePool struct {
}
type ObjPool struct {
bufChan chan *ResourcePool
}
func NewObjPool(numObj int) *ObjPool {
objPool := ObjPool{}
objPool.bufChan = make(chan *ResourcePool, numObj)
for i := 0; i < numObj; i++ {
objPool.bufChan <- &ResourcePool{}
}
return &objPool
}
func (p *ObjPool) GetObj(timeout time.Duration) (*ResourcePool, error) {
select {
case ret := <-p.bufChan:
return ret, nil
case <-time.After(timeout):
return nil, errors.New("timeout")
}
}
func (p *ObjPool) ReleaseObj(obj *ResourcePool) error {
select {
case p.bufChan <- obj:
return nil
default:
return errors.New("overflow")
}
}
func TestObjPool(t *testing.T) {
pool := NewObjPool(10)
for i := 0; i < 11; i++ {
if v, err := pool.GetObj(time.Second * 1); err != nil {
t.Error(err)
} else {
t.Logf("get obj: %p", v)
if err := pool.ReleaseObj(v); err != nil {
t.Error(err)
}
}
}
fmt.Println("Done")
}