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(人员招募中...)

  • 相关阅读:
    C语言中返回字符串函数的四种实现方法
    (转)大整数除法jva.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result异常的解决方法
    @Transactional使用try->catch捕获异常并回滚方法
    Golang1.13.x 解决go get 无法下载问题
    Zookeeper:Unable to read additional data from client sessionid 0x00, likely client has closed socket
    解决Linux系统下面javamelody图片中文乱码问题
    mybatis查询mysql的datetime类型数据时间差了14小时
    以太坊多重钱包离线签名
    Solidity智能合约如何判断地址为0或空
    Solidity开发注意
  • 原文地址:https://www.cnblogs.com/Juvy/p/2285403.html
Copyright © 2011-2022 走看看