在很多程序中都使用到了一些特定资源,比如数据库连接、线程句柄、IMAP连接、LDAP连接等等。在单线程环境下,可能这些资源还能比较容易的管理并分配,但在多线程环境中,管理并发的资源获取和资源释放不是一件容易的事。因此,一般情况下,会在这些程序内部实现一个连接池或者线程池以达到在多线程环境下管理这些资源的目的。
但是这些实现一般都针对特定类型的资源,从一个数据库连接池的实现转移到一个线程池的实现,往往意味着代码的复制,可重用性很差。基于此目的,本文实现了一个基于策略(Policy-Based)的通用资源池。
本文假定读者掌握C++、模板、STL,并对Policy-Based编程有初步的了解,关于Policy-Based编程可参考《Modern C++ Design》。本文参考了Rohit Joshi的文章《Generic Pool: Policy based design》。
首先我们需要定义这样的一个通用资源池所应该具有的特性:
1. 支持通用类型。我们可以像STL那样针对不同类型有各自不同的资源池,例如:Pool<Connection>就是一个连接池,而Pool<Thread>就是一个线程池。
2. 对于在获取资源不足时,需要有两种不同的策略,一种情况是一但遇到资源不足,则立刻抛出异常;另一种情况是一但遇到资源不足,则阻塞当前线程,直到有其他线程释放了资源。
3. 在初始化或者销毁资源池时,用户可能想要做一些额外的初始化工作或销毁工作,需要允许用户能够自定制资源的创建行为。
4. 对于多线程访问,需要使用互斥体来保证线程安全性,同时,这些互斥体的类型通常也应该是可自由定制的。
5. 有些资源池可能需要是全局的。
接下来,我们针对每条特性,并同时结合源代码,来介绍该通用资源池的实现。
Ø 对于特性1,我们可以通过使用模板来比较容易的实现。
template <class Object,
class LackPolicy = NonBlock,
template<class>class CreationPolicy = ObjectCreationDefault,
class MutexType = IceUtil::RecMutex,
typename LackPolicyTraits = LackTraits<MutexType,LackPolicy>
>
class Pool
{
public:
//主要的接口
void initialize(unsigned int nPoolSize = 1);
void destroy();
Object& fetch();
void release(Object& obj);
unsigned int availableNo();
};
Pool类的模板的参数虽然比较多,但大部分都设置有默认参数,因此在使用该Pool时,可以仅仅只指定第一个参数,也就是资源池中具体的资源类型。例如:
typedef Pool<SAConnection> SAConnectionPool;
Ø 对于特性2,参见上文中Pool类的声明,其第二个模板参数LackPolicy控制了资源池在资源不足的情况时所使用的策略。
//方式一:非阻塞且抛出异常(默认模式)
struct NonBlock{};
//方式二:阻塞且等待可用
template<template<class>class MonitorType = IceUtil::Monitor>
struct Block{};
在默认情况下,会使用非阻塞的方式(NonBlock),即一但资源不足,会抛出异常。用户也可以指定使用阻塞方式(Block),即一但资源不足,挂起当前线程直到有其他的线程归还资源。在指定阻塞方式时,用户须同时指定所使用的条件变量类型(阻塞方式使用了线程间条件变量通信方式),同样,默认的条件变量类型为IceUtil::Monitor。可以像如下方式声明阻塞方式:
typedef Pool<SAConnection,Block<>> SAConnectionPool;
Ø 对于特性3,同样参见Pool类声明,其第三个模板参数CreationPolicy指定了在资源初始化和销毁时的行为。一般情况下,使用默认的创建方式ObjectCreationDefault。ObjectCreationDefault声明如下:
//默认的构建方式,若希望有自己构建方式,可自定制
template<class Object>
struct ObjectCreationDefault
{
static Object* Create()
{
return new Object();
}
static void Destroy(Object* pObj)
{
if(pObj)
{
delete pObj;
pObj = NULL;
}
}
static void Check(Object* pObj)
{
//do nothing
}
};
ObjectCreationDefault类中共有三个函数Create、Destroy、Check。Create函数用于资源的创建,默认的行为是直接调用资源的默认构造函数。Destroy函数用于资源的销毁,默认的行为是直接调用资源的析构函数。Check函数用于资源的有效性检查,在每次从资源池中获取资源时,会先进行有效性检查,默认的行为是不做任何检查。若用户希望能够自定制构建行为,需创建新的类并实现这3个函数。
Ø 对于特性4,同样参见Pool类声明,其第四个模板参数MutexType指定了互斥体的类型,默认情况下使用IceUtil::RecMutex作为互斥体,而Pool类的实现则是使用了该互斥体以保证线程安全。
Ø 对于特性5,Pool类没有考虑全局调用。但通过另一种单件模式的封装,可间接达到全局变量的作用。
typedef Pool<SAConnection> SAConnectionPool;
typedef SingletonHolder<SAConnectionPool> CnPoolMgr;
这样声明之后,用户可以在程序的任何地方使用这个全局的资源池。调用的方式形如:
CnPoolMgr::getInstance().initialize(2);
至此,基于策略的通用资源的大致实现和基本用法就介绍完了。
附上所有源代码: