关于对象池的设计及实现

发表于2019-03-19
评论0 1.1k浏览
都知道对象池在减少内存开销上的作用,那在本篇中我们就和大家探讨下关于对象池的设计以及实现。

对象池概述:

对象池模型创建并拥有固定数量的对象,当程序需要一个新的对象时,如果对象池中有空闲对象,则立即返回,否则才创建新的该类对象。当一个对象不再被使用时,其应该应该将其放回对象池,以便后来的程序使用。由于系统资源有限,一个对象池模型应该指定其可容纳的最大对象数量。当达到该数量时,如果仍然有对象创建请求,则抛出异常或者阻塞当前调用线程,直到一个对象被放回对象池中。

对象池模型适用的场景:
  1. 需要使用大量对象
  2. 这些对象的实例化开销比较大且生存期比较短

对象池优势:

一个对象池可以在可容忍时间内创建成功并投入使用。但是创建对象时并不总是这样,尤其是当这些对象的创建过程比较耗时,而且创建和销毁频率又比较大时更是如此。比如数据库连接、网络套接字连接、线程对象、诸如字体或位图等图像对象等。

实现:

假设有如下类定义:
class Object
{
public:
    Object(const string& name) : name_(name)
    {
        printf("Construct Object[%p] %s.\n", this, name_.c_str());
    }
    ~Object()
    {
        printf("~Destruct Object[%p] %s.\n", this, name_.c_str());
    }
    const string& key() const { return name_; }
private:
    string name_;
};

如下对象池类的设计,用来提供Object类对象,分为2个版本介绍:(注意,如下仅考虑了对象池本身所涉及的特性,没有涉及同步控制机制)

版本1:
class ObjectPool
{
public:
    boost::shared_ptr<Object> get(const string& key)
    {
        boost::shared_ptr<Object> pObject;
        boost::weak_ptr<Object>& k_object = objects_[key];
        pObject = k_object.lock();
        if (!pObject)
        {
            pObject.reset(new Object(key),
                         boost::bind(&ObjectPool::releaseObject, this, _1));
            k_object = pObject;
        }
        return pObject;
  }
private:
    void releaseObject(Object* object)
    {
        printf("releaseObject[%p].\n", object);
        if (object)
        {
            objects_.erase(object->key());
        }
        delete object;
    }
    std::map<string, boost::weak_ptr<Object> > objects_;
};

ObjectPool的get函数返回map中key对应的Object对象。如果该对象不存在,则新建一个Object,将其放入map中,然后返回这个新建的Object。同时,重置shared_ptr(新增Object对象)时指定析构器releaseObject,使得对象析构时执行releaseObject(object);

但是上述实现存在一个问题:
将this传入bind函数中,如果ObjectPool对象先于Object对象析构了,那么在析构Object对象时,如何调用releaseObject函数呢?(因为releaseObject函数属于ObjectPool类)

版本2:
class ObjectPool : public boost::enable_shared_from_this<ObjectPool>
{
public:
    boost::shared_ptr<Object> get(const string& key)
    {
        boost::shared_ptr<Object> pObject;
        boost::weak_ptr<Object>& k_object = objects_[key];
        pObject = k_object.lock();
        if (!pObject)
        {
            pObject.reset(new Object(key),
                         boost::bind(&ObjectPool::releaseObject, shared_from_this(), _1));
            k_object = pObject;
        }
        return pObject;
  }
private:
    void releaseObject(Object* object)
    {
        printf("releaseObject[%p].\n", object);
        if (object)
        {
            objects_.erase(object->key());
        }
        delete object;
    }
    std::map<string, boost::weak_ptr<Object> > objects_;
};

要解决版本1中的问题,只需增加ObjectPool的寿命就可以了。可以利用boost::enable_shared_from_this模板类中的shared_from_this(),如此可以将this转换为shared_ptr<ObjectPool>。如此,由于bind是值传递语义,因此其必然保存一份shared_ptr<ObjectPool>的副本,可以保证shared_ptr的引用计数不为0。

测试用例:
// object_pool.cc
#include <map>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <stdio.h>
using std::string;
const int MAXNUM = 5;  // the largest amounts of objects
int nums = 0;          // the current amounts of objects
class Object
{
public:
    Object(const string& name) : name_(name)
    {
        printf("Construct Object[%p] %s.\n", this, name_.c_str());
    }
    ~Object()
    {
        printf("~Destruct Object[%p] %s.\n", this, name_.c_str());
    }
    const string& key() const { return name_; }
private:
    string name_;
};
namespace version1
{
class ObjectPool
{
public:
    boost::shared_ptr<Object> get(const string& key)
    {
        boost::shared_ptr<Object> pObject;
        boost::weak_ptr<Object>& k_object = objects_[key];
        pObject = k_object.lock();
        if (!pObject)
        {
            ++nums;
            BOOST_ASSERT(nums <= MAXNUM);
            pObject.reset(new Object(key),
                         boost::bind(&ObjectPool::releaseObject, this, _1));
            k_object = pObject;
        }
        return pObject;
  }
private:
    void releaseObject(Object* object)
    {
        printf("releaseObject[%p].\n", object);
        if (object)
        {
            --nums;
            objects_.erase(object->key());
        }
        delete object;
    }
    std::map<string, boost::weak_ptr<Object> > objects_;
};
}
namespace version2
{
class ObjectPool : public boost::enable_shared_from_this<ObjectPool>
{
public:
    boost::shared_ptr<Object> get(const string& key)
    {
        boost::shared_ptr<Object> pObject;
        boost::weak_ptr<Object>& k_object = objects_[key];
        pObject = k_object.lock();
        if (!pObject)
        {
            ++nums;
            BOOST_ASSERT(nums <= MAXNUM);
            pObject.reset(new Object(key),
                         boost::bind(&ObjectPool::releaseObject, shared_from_this(), _1));
            k_object = pObject;
        }
        return pObject;
  }
private:
    void releaseObject(Object* object)
    {
        printf("releaseObject[%p].\n", object);
        if (object)
        {
            --nums;
            objects_.erase(object->key());
        }
        delete object;
    }
    std::map<string, boost::weak_ptr<Object> > objects_;
};
}
int main()
{
    boost::shared_ptr<version1::ObjectPool> op1(new version1::ObjectPool);
    boost::shared_ptr<Object> object1 = op1->get("object1");
    boost::shared_ptr<Object> object2 = op1->get("object2");
    boost::shared_ptr<Object> object3 = op1->get("object3");
    boost::shared_ptr<Object> object4 = op1->get("object4");
    boost::shared_ptr<Object> object5 = op1->get("object5");
    boost::shared_ptr<Object> object6 = op1->get("object5");
    //boost::shared_ptr<version2::ObjectPool> op2(new version2::ObjectPool);
    //boost::shared_ptr<Object> object7 = op2->get("object2");
    //boost::shared_ptr<Object> object8 = op2->get("object2");
    return 0;
}
// output
Construct Object[003e1060] object1.
Construct Object[003e10e0] object2.
Construct Object[003e1160] object3.
Construct Object[003e11e0] object4.
Construct Object[003e1260] object5.
releaseObject[003e1260].
~Destruct Object[003e1260] object5.
releaseObject[003e11e0].
~Destruct Object[003e11e0] object4.
releaseObject[003e1160].
~Destruct Object[003e1160] object3.
releaseObject[003e10e0].
~Destruct Object[003e10e0] object2.
releaseObject[003e1060].
~Destruct Object[003e1060] object1.

以上的内容是关于对象池的设计与实现的全部内容,如果还想再深入的了解对象池,可以参考《深入理解对象池技术》。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引