实体池实现

实体池实现

引题

这里主要讲解不仅仅是实体池的实现,还有借鉴的编码规则,注视的格式,培养自己在以后的编码过程中养成好的习惯。

接口

池的设计,主要的就是取出和放回操作。每一个模块都需要有一定的自我
监控能力,可以反应自身的使用情况,因此真对实体池,我们提出实体总量和使用情况。接口具体如下:


// 实体池的接口类型。
type Pool interface {
Take() (Entity, error) // 取出实体
Return(entity Entity) error // 归还实体。
Total() uint32 // 实体池的容量。
Used() uint32 // 实体池中已被使用的实体的数量。
}

实现

在实现的过程中,需要计数实体总量,标志实体池的类型,因为要实现扩展性,我们需要外部提供一个函数作为实现实体的标准。还有容纳器包含实体,可以链表活着其他的,但是chan类型似乎更加的友好做为无状态链表操作。还有一个map用于标识实体是否正在用。当然作为并发组件,线程安全是必要的。

// 实体池的实现类型。
type myPool struct {
    total       uint32          // 池的总容量。
    etype       reflect.Type    // 池中实体的类型。
    genEntity   func() Entity   // 池中实体的生成函数。
    container   chan Entity     // 实体容器。
    idContainer map[uint32]bool // 实体ID的容器。
    mutex       sync.Mutex      // 针对实体ID容器操作的互斥锁。

我们需要为每一个结构体提供NewXXX的函数,用于创建。注意这里创建的时候需要同时提供初始化整个实体池,这个池比较简单,是固定大小的,没有动态创建实体。灵活性不高,其实golang的包中提供了这种类似的池操作,大家可以看看源码如何实现。

// 创建实体池。
func NewPool(
    total uint32,
    entityType reflect.Type,
    genEntity func() Entity) (Pool, error) {
    if total == 0 {
        errMsg :=
            fmt.Sprintf("The pool can not be initialized! (total=%d)\n", total)
        return nil, errors.New(errMsg)
    }
    size := int(total)
    container := make(chan Entity, size)
    idContainer := make(map[uint32]bool)
    for i := 0; i < size; i++ {
        newEntity := genEntity()
        if entityType != reflect.TypeOf(newEntity) {
            errMsg :=
                fmt.Sprintf("The type of result of function genEntity() is NOT %s!\n", entityType)
            return nil, errors.New(errMsg)
        }
        container <- newEntity
        idContainer[newEntity.Id()] = true
    }
    pool := &myPool{
        total:       total,
        etype:       entityType,
        genEntity:   genEntity,
        container:   container,
        idContainer: idContainer,
    }
    return pool, nil
}

下面直接给出整个接口函数的实现,不做一一讲解

func (pool *myPool) Take() (Entity, error) {
    entity, ok := <-pool.container
    if !ok {
        return nil, errors.New("The inner container is invalid!")
    }
    pool.mutex.Lock()
    defer pool.mutex.Unlock()
    pool.idContainer[entity.Id()] = false
    return entity, nil
}
func (pool *myPool) Return(entity Entity) error {
    if entity == nil {
        return errors.New("The returning entity is invalid!")
    }
    if pool.etype != reflect.TypeOf(entity) {
        errMsg := fmt.Sprintf("The type of returning entity is NOT %s!\n", pool.etype)
        return errors.New(errMsg)
    }
    entityId := entity.Id()
    casResult := pool.compareAndSetForIdContainer(entityId, false, true)
    if casResult == 1 {
        pool.container <- entity
        return nil
    } else if casResult == 0 {
        errMsg := fmt.Sprintf("The entity (id=%d) is already in the pool!\n", entityId)
        return errors.New(errMsg)
    } else {
        errMsg := fmt.Sprintf("The entity (id=%d) is illegal!\n", entityId)
        return errors.New(errMsg)
    }
}
// 比较并设置实体ID容器中与给定实体ID对应的键值对的元素值。
// 结果值:
//       -1:表示键值对不存在。
//        0:表示操作失败。
//        1:表示操作成功。
func (pool *myPool) compareAndSetForIdContainer(
    entityId uint32, oldValue bool, newValue bool) int8 {
    pool.mutex.Lock()
    defer pool.mutex.Unlock()
    v, ok := pool.idContainer[entityId]
    if !ok {
        return -1
    }
    if v != oldValue {
        return 0
    }
    pool.idContainer[entityId] = newValue
    return 1
}
func (pool *myPool) Total() uint32 {
    return pool.total
}
func (pool *myPool) Used() uint32 {
    return pool.total - uint32(len(pool.container))
}

感悟

主要感悟在于编码的格式,在哪里写注释,在哪里定义,变量的命名格式都有了新的了解和学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值