经典的0.001秒,让程序回复正常的0.001秒。

本文出现的源头还得从DbHelper说起,先来说说这个DbHelper的演化(产生)过程:

(一)、说起DbHelper大家都非常的熟悉了,就是一个数据库操作帮助类,如果说简单的话,几个静态的方法:

1)、public static DataSet ExecNomQuery(parameters....);

2)、public static DataTable ExecNomQuery(parameters....);

3)、public static bool ExecCommand(parameters....);

4)、public static object ExecScalar(parameters....);

5)...

(二)、如果没有特殊要求的话上面4个方法基本上就够用了,但是如果涉及到事务处理等操作,上面这个数据库帮助类就会显得有点力不从心了。也是正是由于如此,自己把原来的静态数据库帮助类进行了修改,全部采用实例成员,采用new的方式来创建DbHelper,用完后采用using方式将对象Dispose掉。程序运行效果也还可以,如果不是非常高负荷的应用,这样的DbHelper基本上都可以应付。

(三)、为了不至于到处出现new DbHelper()的操作,于是就产生了创建DbHelper对象的简单工厂类。

(四)、在不断应用的过程中,需要对数据库帮助类进行扩展,于是原本只支持MSSqlServer数据库的DbHelper又开始不够用了,为了不改变太多原来DbHelper的代码,于是直接从DbHelper中抽象出一个IDbHelper接口出来,将原来的DbHelper改名为MSSqlDbHelper,同时创建了一个支持Oracle数据库的OracleDbHelper,这样就可以根据通过工厂类创建自己需要的数据库帮助类了(MSSqlServer、OracleDbHelper,如果需要的话,可以自己扩展,只需要继承IDbHelper接口即可)。

(五)、为了消除数据库帮助类中的if;else if;分支的个数,于是产生了抽象工厂类IDbHelperFactory,先通过反射创建IDbHelperFactory,然后通过工厂类创建IDbHelper。(注:抽象工厂类彻底的消除简单工厂类的if-else if分支)

(六)、后来有一段时间一头钻入Ioc的漩涡中,于是产生了一个想法,想自己写一个Ioc容器,。。。哗啦哗啦。。。经过几天的折腾,一个自己打造的精简版的Ioc容器诞生了,通过构造函数/或者setter方式注入创建IDbHelperFactory,然后创建IDbHelper对象。

(七)、上面的IDbHelper用了一段时间,感觉还不错,但是不知不觉的使用过程中,DbHelper中增加了一个又一个的字段、属性、方法(都是实例成员),导致DbHelper对象不断膨胀,从而使得DbHelper实例化的时候,对内存的开销相比最原始的静态数据库帮助类大了许多,想到自己的Ioc容器创建对象的生命周期中有一种是Pooled类型的,即组件池模式。对于这个组件池模式,在这里好好解释一下(对Ioc有一定研究的人可以飘过),组件池模式,即容器在讲对象创建完以后,同时保存一份对象的引用在自己的Pool中,这样调用方在对象调用完毕之后,GC不会将对象回收掉,因为Pool中还有一个该对象的引用。这样在下一个创建该类对象的调用中,Ioc容器就可以先在Pool中寻找是否有可用的对象,如果有的话,就直接返回Pool中的对象,否则重新创建对象。好,问题产生了:

1)、Ioc容器如何判断哪些对象已经不在使用中;

2)、调用方在什么时候释放掉从容器中获取到的对象实例。

基于以上两点,我们可以参考微软自带的数据库连接池功能,即使用的时候,将对象标记为使用中(Status=Open),使用完毕后,将数据库连接对象Close掉,以便其他地方调用。于是一个简单的接口产生了,命名为“可重用对象”:

 

1  public  interface IReusedObject
2 {
3      bool IsOnUsing{ get; set;}
4 }

 

该接口只有一个bool类型的状态属性:IsOnUsing,用于标记该对象是否处于使用中状态。

下面给出从对象池中获取对象的代码构架:

 

// 全局变量,对象池
IList<IReusedObject > _ListInstance;
// 对象池线程安全锁
object _Lock;
// 对象池容量
int _Capacity;

public IocContainer()
{
     this._ListInstance =  new List<IReusedObject >();
     this._Lock =  new  object();
     // 默认设置组件池容量为10,也可以通过其他方式来修改容量
     this._Capacity =  10
}

public  object GetInstance(Type t,....)
{
    IReusedObject retValue;
     lock( this._Lock)
    {
         foreach(IReusedObject obj  in  this._ListInstance)
        {
             if(!obj.IsOnUsing)
            {
                retValue = obj;
                 break;
            }
        }
         if(retValue !=  null)
        {
            retValue.IsOnUsing =  true;
             return retValue;
        }
         // 如果容器的的容量还为超过指定容量大小,则可以重新创建对象实例。
         if( this._ListInstance.Count <  this._Capacity)
        {
             // 通过类型创建该类型的对象实例
            
// 该方法由容器辅助方法完成,在这里就省略了
            retValue = CreateInstance(t);
            retValue.IsOnUsing =  true;
             this._ListInstance.Add(retValue);
             return retValue;
        }
         // 否则递归调用方法再次从容器中获取对象实例
         else
        {
             //这就是那经典的0.001秒         
            Thread.Sleep(1);
            return  this.GetInstance(t,....);
        }
    }
}

 

写完上述方法后心中窃喜了一阵子,于是迫不及待的创建了30(只要大于组件池的容量即可)个线程来测,结果还没等我反应过来,就发现代码出现了死循环程序提示内存溢出了。。。。

着实让我悲剧了一阵子。。。。。。。。。。。。实在不知道为什么会出现这样的问题,难道是:

【先声明:我测试的对象就是上面的IDbHelper,DbHelper继承了IDispose和IReusedObject两个接口,并且在Dispose方法中加了“IsOnUsing = false;",所以通过using(IDbhelper dbHelper = ....){.....}的方式调用DbHelper对象使用完毕后,对象都会被对象池重用。】

1)、组件池中的所有的对象都一直被占用在。。。。。

2)、还是什么其他原因。。。。

答案已经给出:就在代码中红色标识处。

 

经过大家的激烈讨论,发现有一个地方可以优化的:那就是如果调用方忘记经从Ioc容器中获取的对象Dispose()掉的话,那么这个对象就是个死角,永远也不会被释放掉,直到程序退出为止。所以下一步会针对这个Ioc容器进行优化,为组件池中的组件增加对象超时时间(即对象空闲一段时间就将该对象从组件池中释放掉)。


下面贴出优化后的代码,欢迎大家指正,谢谢!

public  override  object GetInstance()
{
    IReusedObject retValue =  null;
    DateTime now;
     // 累计循环次数
     int count =  3;
     // 循环因子
     int i =  0;
     // 先到组件池中查找可用的对象
     lock ( this._Lock)
    {
         while ( true && i++ < count)
        {
            now = DateTime.Now;
             // 先从组件池中获取对象
            retValue =  this._ListInstance.Find(obj => !obj.IsOnUsing);
             // 如果能够获取到,则直接返回
             if (retValue !=  null)
            {
#if DEBUG
Common.InnerLogHelper.WriteLog( " Get instance from pool by PooledIocType[Current Count =  " +  this.InstanceCount.ToString() +  " ]. "" SAS.Utilities.IOC.IocType.GetInstance() ");
#endif
                retValue.LastVisitTime = now;
                retValue.IsOnUsing =  true;
                 break;
            }
             // 否则,如果组件池的还没装满,则重新创建对象实例,并放入组件池中
             else  if ( this._ListInstance.Count <  this.Capacity)
            {
                retValue =  base.CreateInstance()  as IReusedObject;
                 if (retValue ==  null)
                {
                     throw  new Exception( " Type[ " +  base.Type.FullName +  " ] must implement from IReusedObject interfact. ");
                }
                 else
                {
#if DEBUG
Common.InnerLogHelper.WriteLog( " Create a new instance by PooledIocType[Current Count =  " +  this.InstanceCount.ToString() +  " ]. "" SAS.Utilities.IOC.IocType.GetInstance() ");
#endif
                     this._ListInstance.Add(retValue);
                     break;
                }
            }
             // 否则移除组件池中超时的对象,然后进入下一次循环
             else
            {
                 // 移除超时对象
                 this._ListInstance.RemoveAll(obj => obj.LastVisitTime.AddMilliseconds(obj.TimeOut) < now);
                Thread.Sleep( 10);
                 continue;
            }
        }
    }
     if (retValue ==  null)
    {
         throw  new Exception( " Get obj from pool timeout. ");
    }
     return retValue;
}

 

 

ASP.NET开发技术交流群: 67511751(人员招募中...)

你可能感兴趣的:(程序)