数据库连接池, 线程池等等的概念, 相信大家不会陌生. 在编写服务器程序时, 这些概念的应用犹如家常便饭. 至于为什么要用”池”, 通俗的讲, 就是”避免频繁的资源分配与释放, 从而提高程序性能”, 往抽象里说, 就是”用空间换时间”.
下面将介绍一个通用资源池的实现. 受益于shared_ptr的自定义deleter, 它的接口极为简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | template<typename R> struct ResourcePool: boost::noncopyable{ ///资源类型 typedef R ValueType; ///资源的智能指针 typedef boost::shared_ptr<R> ValuePtr; ///得到池的大小(包括正被使用的, 和闲置的资源个数) std::size_t size()const; ///得到闲置的资源个数 std::size_t unused()const; ///释放闲置的资源 void shrink(); ///获取资源, 如果有闲置资源, 就直接使用闲置资源, 如果没有就分配一个(分配失败的时候会返回空指针) ValuePtr get(); template<typename A> explicit ResourcePool(A allocator); }; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| struct Deleter{ Deleter(const ProxyWeakPtr & proxy, const ValuePtr & res): _proxy(proxy), _res(res) {} void operator()(void*){ /// try lock proxy ProxyPtr proxy = _proxy.lock(); if(proxy) proxy->recycle(_res); ///else, the resource pool gone. nothing to do, and _res will do the right thing. } private: /// proxy weak pointer ProxyWeakPtr _proxy; /// the real resource ValuePtr _res; }; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| boost::shared_ptr<int> alloc(){ return boost::shared_ptr<int>(new int(1)); } int main(){ lite::util::ResourcePool<int> pool(alloc); ///获取一个资源 boost::shared_ptr<int> i = pool.get(); assert(*i == 1); assert(pool.size() == 1); assert(pool.unused() == 0); ///释放获得的资源 i.reset(); assert(pool.unused() == 1); ///清理闲置资源 pool.shrink(); assert(pool.size() == 0); assert(pool.unused() == 0); return 0; } |
其中最核心的一个方法就是get. 你可能已经发现了, 这里没有回收的方法. 那么资源是怎么被回收, 从而重复利用的呢? 关键在于get的返回值, get返回一个资源的智能指针, 这个指针指针的deleter是定制的
为了降低Deleter和ResourcePool的耦合, 这里引入了一个Proxy类… 越讲越复杂了, 还是看完整代码和示例吧
完整代码下载