對象池概述:
對象池模型創建並擁有固定數量的對象,當程序需要一個新的對象時,如果對象池中有空閑對象,則立即返回,否則才創建新的該類對象。當一個對象不再被使用時,其應該應該將其放回對象池,以便后來的程序使用。由於系統資源有限,一個對象池模型應該指定其可容納的最大對象數量。當達到該數量時,如果仍然有對象創建請求,則拋出異常或者阻塞當前調用線程,直到一個對象被放回對象池中。
對象池模型適用的場景:
(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