对象池、数据库连接池原理
一、一点说明:
(本文的知识点来自C#线程参考手册)
对象池在企业软件中非常常见,为了提供应用的性能,必须控制对象实例的创建。比如创建数据库连接对象,每一次创建的代价非常高昂,每一次使用数据库都需要连接数据库,代价非常高昂,因此有了数据库连接池,每一次一个连接被创建之后不是就马上释放,下一次使用的时候重新创建,而是在使用完毕之后重新放回数据库连接池,当需要再次使用数据库的时候,从数据库连接池拿取空闲的数据库连接。
需要数据库连接池的原因:
1、创建一个对象的代价高昂:
- 创建的时候占用较多的内存,内存的分配会降低应用程序的性能
- 需要使用较长的时间
2、创建的对象具备可复用性
二、实现对象池:
一个对象池的创建:
- 两个存放对象的集合:一个存放正在使用的对象集合,一个存放空闲的对象集合;
- 两个变量:一个变量值指定为空闲对象集合进行回收释放内存和资源的时间间隔,一个变量为最后一次对空间对象进行回收的时间;
- 一个创建对象的方法
- 一个检查对象是否空闲的方法啊
- 一个对需要回收释放资源的对象进行失效的方法;
- 一个从对象池取对象的方法;
- 一个把对象放回对象池的方法;
- 一个定期回收空闲对象集合中需要失效的对象。
下面看基类:
public abstract class ObjectPool { private long _lastCheckOutTime; private static Dictionary<object, long> lockObject; private static Dictionary<object, long> freeObject; internal static long GARBAGE_INTERVAL = 2 * 60 * 1000; // 静态构造函数,先于其他构造函数被调用 static ObjectPool() { lockObject = new Dictionary<object, long>(); freeObject = new Dictionary<object, long>(); } internal ObjectPool() { _lastCheckOutTime = DateTime.Now.Ticks; Timer timer = new Timer(new TimerCallback(CollecttGarbage),null,0,GARBAGE_INTERVAL); } protected abstract object Create(); protected abstract bool Validate(object o); protected abstract void Expire(object o); internal object GetObjectFromPool() { long now = DateTime.Now.Ticks; _lastCheckOutTime = now; object o = null; lock (this) { try { foreach (var item in freeObject) { o = item.Key; if (Validate(o)) { freeObject.Remove(item); lockObject.Add(o, now); return item; } else { freeObject.Remove(o); Expire(o); o = null; } } }catch(Exception ex) { o = Create(); lockObject.Add(o, now); } } return o; } internal void ReturnObjectToPool(object o) { if (o != null) { lock (this) { lockObject.Remove(o); freeObject.Add(o, DateTime.Now.Ticks); } } } protected virtual void CollecttGarbage(object state) { lock (this) { object o; long now = DateTime.Now.Ticks; try { foreach (var item in freeObject) { o = item.Key; if ((now - item.Value) > GARBAGE_INTERVAL) { freeObject.Remove(item); Expire(o); o = null; } } }catch(Exception ex) { } } } }
从代码可以看出:
1、我们在基类实现的三个方法:CollecttGarbage ReturnObjectToPool GetObjectFromPool都是加了锁的,原因是在执行这三个方法的时候,操作的对象集合可能同时被操纵(减少、增加、或者移除),而在这三个方法中我们是不希望出现竞争条件的。
2、Create Expire Validate 三个方法都是虚方法,在基类里面没有进行实现,因为这三个方法是需要根据不同的业务场景根据不同类型的对象池编写的。另外,这三个方法的逻辑控制使用了模板操作方法设计模式。
下面看数据库连接池的简单实现:
public sealed class DBConnectionSingleton:ObjectPool { // 私有化的构造函数 private DBConnectionSingleton() { } // 使用单例模式,创建唯一对象。 public static readonly DBConnectionSingleton instance = new DBConnectionSingleton(); private static string ConnectionString { get; } protected override bool Validate(object o) { try { SqlConnection temp = new SqlConnection(ConnectionString); return !(temp.State.Equals(System.Data.ConnectionState.Closed)); }catch(SqlException ex) { return false; } } protected override void Expire(object o) { try { ((SqlConnection)o).Close(); }catch(Exception ex) { } } protected override object Create() { SqlConnection conn = new SqlConnection(ConnectionString); conn.Open(); return (conn); } }
end===