对象池模式

对象池模式是一种 creational 设计模式。该模式下,池中所包含的对象将不再是每次需要时创建,使用完毕后销毁,而是初始化好后放入池中等待使用。使用该池的 client 每次需要时都会向对象池请求一个对象,在上面进行一些操作,使用完之后返回给对象池。这个过程可以是手动执行或自动执行。使用对象池的目的主要是为了提升性能,尤其是当需要和一些创建昂贵但使用时间不长的对象打交道时。

初始状态下,池模式创建了一个可复用的初始化好的对象的集合。当 client 需要一个新对象时,它会向对象池发起请求,如果已经有可使用的对象,则立刻返回一个对象。如果当前池中没有可使用的对象,则会创建一个然后返回。一些对象池使用的资源型的对象会有最大个数限制,如果达到个数后有新的请求时,可以选择抛出异常,或 block 当前请求的线程直到有对象可使用,也可以提升对象池的限制数。

对象池模式的常见使用场景是数据库连接池、网络连接池和线程池等,也可以用在使用第三方资源的地方。

注意事项:

  • 如果创建对象的代价不昂贵,则无需使用。否则反而浪费内存。
  • 使用完毕后返回对象时,不应该存在对它的引用。
  • 返回的对象应该清除自身的状态,否则可能会引起几个问题,一个是上一个 client 的一些状态(比如认证信息等)给了下一个 client,还有就是上一个 client 的一些隐私数据泄露给了下一个 client。
  • 一定要考虑线程安全。

实现

对象池在 C++ 中可以用过 smart pointer 实现。在有 GC 的语言中,由于没有 destructor 的存在,所以要手动实现。不建议使用 finalizer 来归还对象,因为不能确保它什么时候执行。可以使用 usingtry...finnally 来保证即使抛出异常了也能正常归还对象。

C# 中的实现

C# 中可以使用 List<T> 配合锁来实现,由于 List<T> 不是线程安全的,所以要注意多线程的情况。虽然麻烦点,但可以跟踪每一个对象的引用,适用于资源有严格限制的情况:

using System;
using System.Collections.Generic;

namespace ObjectPoolPattern
{
    public class ObjectPool<T>
    {
        private List<T> _available;
        private List<T> _inUse;
        private Func<T> _objectGenerator;
        private Action<T> _objectReset;
        private int _max;

        public ObjectPool(Func<T> objectGenerator, Action<T> objectReset, int max)
        {
            _objectGenerator = objectGenerator;
            _objectReset = objectReset;
            _max = max;
            _available = new List<T>();
            _inUse = new List<T>();
        }

        public T GetObject()
        {
            lock (_available)
            {
                if (_available.Count != 0)
                {
                    var item = _available[0];
                    _inUse.Add(item);
                    _available.RemoveAt(0);
                    return item;
                }
                else
                {
                    if (_inUse.Count == _max)
                    {
                        throw new Exception("没有可用资源");
                    }
                    var newItem = _objectGenerator();
                    _inUse.Add(newItem);
                    return newItem;
                }
            }
        }

        public void ReleaseObject(T item)
        {
            _objectReset(item);

            lock (_available)
            {
                _available.Add(item);
                _inUse.Remove(item);
            }
        }
    }
}

也可以用 ConcurrentBag 类来实现一个简单的,因为它更安全并且效率更高,对于资源不是有限,只是单纯为了减少创建和销毁对象的消耗的情况比较适用,因为实现简单,不需要用到锁:

using System;
using System.Collections.Concurrent;

namespace ObjectPoolPattern
{
    public class ObjectPool<T>
    {
        private ConcurrentBag<T> _objects;
        private Func<T> _objectGenerator;

        public ObjectPool(Func<T> objectGenerator, Action<T> objectReset,)
        {
            if (objectGenerator == null) throw new ArgumentNullException("objectGenerator");
            _objects = new ConcurrentBag<T>();
            _objectGenerator = objectGenerator;
            _objectReset = objectReset;
        }

        public T GetObject()
        {
            T item;
            if (_objects.TryTake(out item)) return item;
            return _objectGenerator();
        }

        public void PutObject(T item)
        {
             _objectReset(item);
            _objects.Add(item);
        }
    }
}

StackOverflow 上有人推荐用 .NET Core 中 System.Buffers 提供的 ArrayPool<T> 来实现对象池。不过按照这个类型本来的目的是给 primitive 或 struct-based 类型使用的一个 Buffer,并不是给昂贵资源的对象用的,这里就不介绍了,可以参考这里

参考

转载于:https://www.cnblogs.com/LeewayHsu/p/10667712.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值