对象池的设计及其实现

对象池概述:

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

对象池模型适用的场景:

(1)需要使用大量对象

(2)这些对象的实例化开销比较大且生存期比较短

对象池优势:

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

实现:

假设有如下类定义:

 1 class Object
 2 {
 3 public:
 4     Object(const string& name) : name_(name)
 5     {
 6         printf("Construct Object[%p] %s.\n", this, name_.c_str());
 7     }
 8
 9     ~Object()
10     {
11         printf("~Destruct Object[%p] %s.\n", this, name_.c_str());
12     }
13
14     const string& key() const { return name_; }
15
16 private:
17     string name_;
18 };

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

版本1

 1 class ObjectPool
 2 {
 3 public:
 4     boost::shared_ptr<Object> get(const string& key)
 5     {
 6         boost::shared_ptr<Object> pObject;
 7         boost::weak_ptr<Object>& k_object = objects_[key];
 8         pObject = k_object.lock();
 9         if (!pObject)
10         {
11             pObject.reset(new Object(key),
12                          boost::bind(&ObjectPool::releaseObject, this, _1));
13             k_object = pObject;
14         }
15         return pObject;
16   }
17
18 private:
19     void releaseObject(Object* object)
20     {
21         printf("releaseObject[%p].\n", object);
22         if (object)
23         {
24             objects_.erase(object->key());
25         }
26         delete object;
27     }
28
29     std::map<string, boost::weak_ptr<Object> > objects_;
30 };

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

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

版本2

 1 class ObjectPool : public boost::enable_shared_from_this<ObjectPool>
 2 {
 3 public:
 4     boost::shared_ptr<Object> get(const string& key)
 5     {
 6         boost::shared_ptr<Object> pObject;
 7         boost::weak_ptr<Object>& k_object = objects_[key];
 8         pObject = k_object.lock();
 9         if (!pObject)
10         {
11             pObject.reset(new Object(key),
12                          boost::bind(&ObjectPool::releaseObject, shared_from_this(), _1));
13             k_object = pObject;
14         }
15         return pObject;
16   }
17
18 private:
19     void releaseObject(Object* object)
20     {
21         printf("releaseObject[%p].\n", object);
22         if (object)
23         {
24             objects_.erase(object->key());
25         }
26         delete object;
27     }
28
29     std::map<string, boost::weak_ptr<Object> > objects_;
30 };

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

测试用例:

  1 // object_pool.cc
  2 #include <map>
  3
  4 #include <boost/bind.hpp>
  5 #include <boost/enable_shared_from_this.hpp>
  6 #include <boost/shared_ptr.hpp>
  7 #include <boost/weak_ptr.hpp>
  8
  9 #include <stdio.h>
 10
 11 using std::string;
 12 const int MAXNUM = 5;  // the largest amounts of objects
 13 int nums = 0;          // the current amounts of objects
 14
 15 class Object
 16 {
 17 public:
 18     Object(const string& name) : name_(name)
 19     {
 20         printf("Construct Object[%p] %s.\n", this, name_.c_str());
 21     }
 22
 23     ~Object()
 24     {
 25         printf("~Destruct Object[%p] %s.\n", this, name_.c_str());
 26     }
 27
 28     const string& key() const { return name_; }
 29
 30 private:
 31     string name_;
 32 };
 33
 34
 35 namespace version1
 36 {
 37
 38 class ObjectPool
 39 {
 40 public:
 41     boost::shared_ptr<Object> get(const string& key)
 42     {
 43         boost::shared_ptr<Object> pObject;
 44         boost::weak_ptr<Object>& k_object = objects_[key];
 45         pObject = k_object.lock();
 46         if (!pObject)
 47         {
 48             ++nums;
 49             BOOST_ASSERT(nums <= MAXNUM);
 50             pObject.reset(new Object(key),
 51                          boost::bind(&ObjectPool::releaseObject, this, _1));
 52             k_object = pObject;
 53         }
 54         return pObject;
 55   }
 56
 57 private:
 58     void releaseObject(Object* object)
 59     {
 60         printf("releaseObject[%p].\n", object);
 61         if (object)
 62         {
 63             --nums;
 64             objects_.erase(object->key());
 65         }
 66         delete object;
 67     }
 68
 69     std::map<string, boost::weak_ptr<Object> > objects_;
 70 };
 71
 72 }
 73
 74 namespace version2
 75 {
 76
 77 class ObjectPool : public boost::enable_shared_from_this<ObjectPool>
 78 {
 79 public:
 80     boost::shared_ptr<Object> get(const string& key)
 81     {
 82         boost::shared_ptr<Object> pObject;
 83         boost::weak_ptr<Object>& k_object = objects_[key];
 84         pObject = k_object.lock();
 85         if (!pObject)
 86         {
 87             ++nums;
 88             BOOST_ASSERT(nums <= MAXNUM);
 89             pObject.reset(new Object(key),
 90                          boost::bind(&ObjectPool::releaseObject, shared_from_this(), _1));
 91             k_object = pObject;
 92         }
 93         return pObject;
 94   }
 95
 96 private:
 97     void releaseObject(Object* object)
 98     {
 99         printf("releaseObject[%p].\n", object);
100         if (object)
101         {
102             --nums;
103             objects_.erase(object->key());
104         }
105         delete object;
106     }
107
108     std::map<string, boost::weak_ptr<Object> > objects_;
109 };
110
111 }
112
113
114 int main()
115 {
116     boost::shared_ptr<version1::ObjectPool> op1(new version1::ObjectPool);
117
118     boost::shared_ptr<Object> object1 = op1->get("object1");
119     boost::shared_ptr<Object> object2 = op1->get("object2");
120     boost::shared_ptr<Object> object3 = op1->get("object3");
121     boost::shared_ptr<Object> object4 = op1->get("object4");
122     boost::shared_ptr<Object> object5 = op1->get("object5");
123     boost::shared_ptr<Object> object6 = op1->get("object5");
124
125     //boost::shared_ptr<version2::ObjectPool> op2(new version2::ObjectPool);
126     //boost::shared_ptr<Object> object7 = op2->get("object2");
127     //boost::shared_ptr<Object> object8 = op2->get("object2");
128
129     return 0;
130 }
131
132 // output
133 Construct Object[003e1060] object1.
134 Construct Object[003e10e0] object2.
135 Construct Object[003e1160] object3.
136 Construct Object[003e11e0] object4.
137 Construct Object[003e1260] object5.
138 releaseObject[003e1260].
139 ~Destruct Object[003e1260] object5.
140 releaseObject[003e11e0].
141 ~Destruct Object[003e11e0] object4.
142 releaseObject[003e1160].
143 ~Destruct Object[003e1160] object3.
144 releaseObject[003e10e0].
145 ~Destruct Object[003e10e0] object2.
146 releaseObject[003e1060].
147 ~Destruct Object[003e1060] object1.

References

https://en.wikipedia.org/wiki/Object_pool_pattern

时间: 2024-10-02 23:22:35

对象池的设计及其实现的相关文章

对象池的设计

对象池的设计及其实现 对象池概述: 对象池模型创建并拥有固定数量的对象,当程序需要一个新的对象时,如果对象池中有空闲对象,则立即返回,否则才创建新的该类对象.当一个对象不再被使用时,其应该应该将其放回对象池,以便后来的程序使用.由于系统资源有限,一个对象池模型应该指定其可容纳的最大对象数量.当达到该数量时,如果仍然有对象创建请求,则抛出异常或者阻塞当前调用线程,直到一个对象被放回对象池中. 对象池模型适用的场景: (1)需要使用大量对象 (2)这些对象的实例化开销比较大且生存期比较短 对象池优势

对象池方案总结

最近项目涉及到大量数据并发的问题,短时间内会产生和销毁大量对象,因此担心会不会影响系统的性能,考虑使用对象池的方案. 但是在对对象池的学习中,对于是否使用对象池产生的疑问. MSDN中 “How to: Create an Object Pool by Using a ConcurrentBag”(https://msdn.microsoft.com/en-us/library/ff458671.aspx)一文中提到 “Object pools can improve application p

对象池实现分析

对象池实现分析 什么是对象池技术?对象池应用在哪些地方? 对象池其实就是缓存一些对象从而避免大量创建同一个类型的对象,类似线程池的概念.对象池缓存了一些已经创建好的对象,避免需要时才创建对象,同时限制了实例的个数.池化技术最终要的就是重复的使用池内已经创建的对象.从上面的内容就可以看出对象池适用于以下几个场景: 创建对象的开销大 会创建大量的实例 限制一些资源的使用 如果创建一个对象的开销特别大,那么提前创建一些可以使用的并且缓存起来(池化技术就是重复使用对象,提前创建并缓存起来重复使用就是池化

Netty轻量级对象池实现分析

什么是对象池技术?对象池应用在哪些地方? 对象池其实就是缓存一些对象从而避免大量创建同一个类型的对象,类似线程池的概念.对象池缓存了一些已经创建好的对象,避免需要时才创建对象,同时限制了实例的个数.池化技术最终要的就是重复的使用池内已经创建的对象.从上面的内容就可以看出对象池适用于以下几个场景: 创建对象的开销大 会创建大量的实例 限制一些资源的使用 如果创建一个对象的开销特别大,那么提前创建一些可以使用的并且缓存起来(池化技术就是重复使用对象,提前创建并缓存起来重复使用就是池化)可以降低创建对

Unity 对象池的使用

在游戏开发过程中,我们经常会遇到游戏发布后,测试时玩着玩着明显的感觉到有卡顿现象.出现这种现象的有两个原因:一是游戏优化的不够好或者游戏逻辑本身设计的就有问题,二是手机硬件不行.好吧,对于作为程序员的我们只能从第一个原因着手了,那就开始对着Profiler看性能开销,接下来就开始做各种内存,特效,代码上的优化了.对于这种问题,有经验的开发者在一开始就会做一个规范的设计,就我们的项目而言,设计时包含了角色池,怪物池,特效池,经验池,伤害池......所谓的对象池就是尽可能的复用内存中已经驻留的资源

C++ 线程池的设计问题

1. 给用户添加任务的接口是 schedule(arg), arg应该如何设置 a) 创建 Work class, 将arg设置为 Work*, Work由用户创建,用户删除,线程池内仅保留对Work对象的引用 问题:我写完thread_pool后才发现,用户不再知道如何去删除他创建的 Work了,因为线程池内保存着指向Work的指针,线程池是自动析构的,因此,这种设计方法不可行 b) 设计方法和 a 一致,唯一的不同是线程池内复制了Work, 在类内,直接使用Work对象,不再使用指针了 问题

Unity3D 基于预设(Prefab)的泛型对象池实现

背景 在研究Inventory Pro插件的时候,发现老外实现的一个泛型对象池,觉得设计的小巧实用,不敢私藏,特此共享出来. 以前也看过很多博友关于对象池的总结分享,但是世界这么大,这么复杂到底什么样的对象池才是好的呢,我们发现通用的对象池未必适应所有的环境,比如基于UI的局部(从某个Scene,到某个Dialog)对象池,范围不同,需要的对象池就有不同的要求.本文就是介绍一种基于预设(Prefab)的局部UI对象池. 通用信息提示窗口的实现http://www.manew.com/thread

android对象池之Message

在android系统中,message常在多线程之间信息交流中用到,通过Handler来传递线程间的消息(message).今天讨论的是android中的message特性:对象池. 在android中,google对message有一句这样的英文说明: * <p class="note">While the constructor of Message is public, the best way to get * one of these is to call {@l

(转载)一个通用并发对象池的实现

原文链接,译文链接,原文作者: Sarma Swaranga,本文最早发表于deepinmind,校对:郑旭东 这篇文章里我们主要讨论下如何在Java里实现一个对象池.最近几年,Java虚拟机的性能在各方面都得到了极大的提升,因此对大多数对象而言,已经没有必要通过对象池来提高性能了.根本的原因是,创建一个新的对象的开销已经不像过去那样昂贵了. 然而,还是有些对象,它们的创建开销是非常大的,比如线程,数据库连接等这些非轻量级的对象.在任何一个应用程序里面,我们肯定会用到不止一个这样的对象.如果有一