zoukankan      html  css  js  c++  java
  • 经典的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(人员招募中...)

  • 相关阅读:
    左偏树
    论在Windows下远程连接Ubuntu
    ZOJ 3711 Give Me Your Hand
    SGU 495. Kids and Prizes
    POJ 2151 Check the difficulty of problems
    CodeForces 148D. Bag of mice
    HDU 3631 Shortest Path
    HDU 1869 六度分离
    HDU 2544 最短路
    HDU 3584 Cube
  • 原文地址:https://www.cnblogs.com/Juvy/p/2285403.html
Copyright © 2011-2022 走看看