Visual C++ 7.1Visual C++ 8.0Visual C++ 7.0Visual Studio .NET 2003Visual C++ 6.0MFCIntermediateDevVisual StudioWindowsC++.NET
基于模板的通用池(使用 C++)






4.29/5 (21投票s)
2004年8月27日
4分钟阅读

124720

1531
基于模板的通用 C++ 对象池。
引言
许多应用程序都使用连接/对象池。一个程序可能需要一个 IMAP 连接池和一个 LDAP 连接池。可以轻松地实现一个 IMAP 连接池,然后复制现有代码来实现一个 LDAP 连接池。程序不断增长,现在需要一个线程池。于是就复制 IMAP 连接池的代码,并将其转换为线程池(复制、粘贴、查找、替换???)。需要对池实现进行一些更改?这并不容易,因为代码已经在多个地方被复制。在鼓励可重用性的面向对象环境中,重复编写源代码并不是一种明智的方法。实现一个可以容纳任何任意类型的池,而不是复制代码,似乎更有意义。如何做到这一点?答案是使用类型参数化,更常称为模板。
C++ 模板允许实现一个具有类型参数 T
的通用 Pool<T>
模板。T
可以被替换为实际类型,例如,类 ImapConn
,C++ 会生成类 Pool<ImapConn>
。更改池的实现将变得相对简单。一旦在模板 Pool<T>
中实现了更改,这些更改将立即反映在类 Pool<ImapConn>
、Pool<LdapConn>
和 Pool<Threads>
中。
附带的演示项目包含
- 文档:源代码文档。
- 源代码和项目文件。
本文演示了如何使用模板实现通用对象池。代码已在 Windows 和 Linux 上编译。请随时修改和使用。
以下是实现通用对象池的要求
- 通用性:它应该足够通用,能够与任何类型的资源一起工作。例如,数据库连接池、线程池、其他资源池等...
- 池大小:池的大小应该是可配置的,并且如果需要,可以在运行时更改。
- 池类型:如果池是固定大小的,或者在池已满的情况下允许临时连接。
- 对象的生命周期:如果用户不将资源交还,对象将在多长时间后被视为已过期并返回到空闲资源池。
- 超时功能:如果池已满且不允许临时连接,调用函数可以等待多久才能获取对象。
源代码包含
- PoolMgr.h:实现了单例 Pool 类。它具有以下函数
template<class T> class PoolMgr { typedef ObjectHolder<T> ObjHolder; typedef list<ObjHolder> ObjList; static Mutex m_mPool; // Mutex for Pool static instance Mutex m_mData; // Mutex for Pool data public: // Get the Instance of pool manager static PoolMgr<T>* GetInstance() { if(!m_pPoolMgr) { Lock<Mutex> gaurd(m_mPool); if(!m_pPoolMgr) m_pPoolMgr = new PoolMgr<T>() } return m_pPoolMgr; } // delete the pool static void DeletePool() { if(m_pPoolMgr) { Lock<Mutex> gaurd(m_mPool); if(m_pPoolMgr){ delete m_pPoolMgr; m_pPoolMgr = NULL; } } // Initliaze pool void Init(unsigned nPoolSize, long nExpirationTime, bool bTempObjAllowed, unsigned nWaitTime = 3) { … } // Reset the pool void ResetPool() { ..... } // Initliaze pool void Init(unsigned nPoolSize, long nExpirationTime, bool bTempObjAllowed, unsigned nWaitTime = 3) { ... } // Checkout the Object from pool T* Checkout() { ... } // checkin the Object into pool void Checkin(T *pObj) { ... } private: static PoolMgr<T> *m_pPoolMgr; // static instance of PoolMgr //private constructor PoolMgr() { m_nPoolSize = 0; m_nExpirationTime = 600; // in sec m_bTempObjAllowed = true; m_nWaitTime = 3; } // distructor ~PoolMgr() { } // pool size : default 0 unsigned m_nPoolSize; // wait time: How long calling function can wait to find object unsigned m_nWaitTime; // Object expiration time: default 600 long m_nExpirationTime; // if pool is full, is tempobject allowed bool m_bTempObjAllowed; // reserved objects ObjList m_oReserved; // free objects ObjList m_oFree; }; template<class T> PoolMgr<T>* PoolMgr<T>::m_pPoolMgr = NULL; //initialize static instance template<class T> Mutex PoolMgr<T>::m_mPool;
static PoolMgr<T>* GetInstance()
:返回PoolMgr
的实例。static void DeletePool()
:删除池并释放资源。void Init(unsigned nPoolSize, long nExpirationTime, bool bTempObjAllowed, unsigned nWaitTime)
:用户必须使用以下参数初始化池PoolSize
:池的大小。ExpirationTime
:持续时间(秒)。如果对象在此持续时间内未使用,则该对象将被视为已过期,并将移至可用对象池。TempConnAllowd
:如果池已满,是否允许池创建临时连接。WaitTime
:如果不允许临时连接且池已满,调用函数可以等待多久才能从已过期连接中获取连接。
void ResetPool()
:释放所有资源并重置池。T* Checkout()
:签出资源。void Checkin(T* pObj)
:签入资源。template<class T>
:PoolMgr
类包含两个池列表。一个用于已保留对象,另一个用于空闲对象。在多线程环境中,这可以避免锁定所有对象,而是锁定特定类型的对象。例如,只锁定所有空闲对象或已保留对象。
- ObjectHolder.h:包含对象指针和时间戳。它是一个类型为
T
的模板类,允许存储任何通用对象类。template<class T> class ObjectHolder { public: // constructor ObjectHolder() { m_nTimeStamp = -1; m_pObj = NULL; } // distructor ~ObjectHolder() { if(m_pObj) { m_pObj->Release(); m_pObj = NULL; } } //Initliaze object void InitObject() { if(!m_pObj) { m_pObj = new T(); m_pObj->Init(); } } private: T *m_pObj; // object pointer long m_nTimeStamp; // timestamp };
- GenericObject.h:这是一个用于测试此池的示例通用类。此池的用户需要在其连接/对象类中实现以下方法,或继承自
GenericObject
类。class GenericObject { public: //constructor GenericObject() {} //destrctor ~GenericObject() {} //Initliaze object virtual void Init() {} //Release the resource related to object virtual void Release() {} // Check if object is still usable virtual bool IsUsable() { return true; } // If object is not usable, make it usable virtual bool MakeUsable() { if(!IsUsable()) { Init(); return true; } };
这里
void Init()
:初始化对象。如果对象需要建立连接,请在此函数中执行。void Release()
:释放资源。bool IsUsabled()
:此对象是否仍然可用?bool MakeUsable()
:如果它不可用,尝试使其可用,如果成功,则返回true
。如果成功使其可重用,这将避免构造新对象。
- MutexWin.h(Windows)和 Mutex.h(Linux)
// Lock class template <class T > class Lock { T& obj_; // type object public: // Lock Lock(T& obj):obj_(obj) { obj_.Lock(); } // Unlock ~Lock() { obj_.Unlock(); } }; // Mutex class class Mutex { public: // constructor Mutex() { InitializeCriticalSection(&m_mMutex); } // destructor virtual ~Mutex() { DeleteCriticalSection(&m_mMutex); } // lock bool Lock() { EnterCriticalSection(&m_mMutex); return true; } // unlock bool Unlock() { LeaveCriticalSection(&m_mMutex); return true; } private: CRITICAL_SECTION m_mMutex; // critical section as mutex void operator=(Mutex &m_mMutex) {} // private = operator Mutex( const Mutex &m_mMutex ) {} // private copy constructor };
- main.c:这是一个示例
main
函数,它获取PoolMgr
的实例,并签出和签入GenericObject
。PoolMgr<GenericObject> *pMgr = PoolMgr<GenericObject>::GetInstance(); if(pMgr) { // Pool size 10, object expiration time 600 sec // and temporary connection not allowed pMgr->Init(10,600, false); GenericObject *pObj = NULL; pObj = pMgr->Checkout(); pMgr->Checkin(pObj); pMgr->ResetPool(); // ExpirationTime test: Pool size is 1, expiration time // is 10 secs, and temporary connections not allowed. // Here when you checkout 2nd object, it would wait for 10 seconds // and the 1st object would be garbage collected // and moved to free resource. pMgr->Init(1,10, false); GenericObject *pObj = NULL; pObj = pMgr->Checkout(); std::cout << "1st Object checked out " << std::endl; pObj = pMgr->Checkout(); std::cout << "2st Object checked out after 10 secs " << std::endl; pMgr->ResetPool(); } PoolMgr<GenericObject>::DeletePool(); // delete and free all resources
请参阅我的另一篇关于对象池设计的文章:通用对象池:基于策略的设计。
历史
- 2004/09/16:添加了对线程同步的支持。
请告诉我如何改进这篇文章,使其更有用。