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

posted on 2011-12-12 23:04 Juvy 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/Juvy/archive/2011/12/12/2285403.html

经典的0.001秒,让程序回复正常的0.001秒。相关推荐

  1. 微信接入服务器源码,经典论坛程序Discuz! Q 1.0 发布:完全开放源码,原生接入微信...

    中文 PC 互联网知名社区开源论坛系统 Discuz! 在过去 15 年间,服务过超过 200 万网站客户.其推出的 UCenter.SupeSite,ECshop 等组件所代表的产品理念对今天移动互 ...

  2. c语言随机延迟5-10秒,linux下写个C语言程序,要求有0.5微秒以下的延时,要怎样写...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #include #include #define B break void de(int timee) { unsi ...

  3. 这 10 种 MySQL 经典错误案例,99% 的程序员一定遇到过!你呢?

    今天就给大家列举 MySQL 数据库中,最经典的十大错误案例,并附有处理问题的解决思路和方法,希望能给刚入行,或数据库爱好者一些帮助,今后再遇到任何报错,我们都可以很淡定地去处理.学习任何一门技术的同 ...

  4. Java黑皮书课后题第7章:**7.24(仿真:优惠券收集问题)优惠券收集问题是一个经典的统计问题。编写程序,模拟要得到四张不同花色的牌所需要的选取次数,然后显示选中的四张牌

    **7.24(仿真:优惠券收集问题)优惠券收集问题是一个经典的统计问题.编写程序,模拟要得到四张不同花色的牌所需要的选取次数,然后显示选中的四张牌 题目 题目描述与运行示例 破题:花色与数字 代码 题 ...

  5. 一对经典的时间获取客户/服务器程序

    前言 本文通过一对经典的时间获取客户/服务器程序,展现了Linux网络编程的大体框架,为以后更深入的学习打下基础. 客户服务器模式网络编程的大体框架 客户端代码 1 #include <stdi ...

  6. c语言怎么让程序停止3秒,求助!!!!用单片机的定时器T1怎么写一个LED亮2秒灭3秒的程序 C语言...

    求助!!!!用单片机的定时器T1怎么写一个LED亮2秒灭3秒的程序 C语言 关注:120  答案:4  mip版 解决时间 2021-02-05 00:55 提问者習慣邇的習慣 2021-02-04 ...

  7. VC6.0(VC++6.0)使用教程(使用VC6.0编写C语言程序)

    VC6.0(VC++6.0)使用教程(使用VC6.0编写C语言程序) Visual C++ 6.0简称VC或者VC6.0,是微软1998年推出的一款C/C++ IDE,界面友好,调试功能强大.VC6. ...

  8. c语言滤出是个最小值,经典滤波算法及C语言程序

    <经典滤波算法及C语言程序>由会员分享,可在线阅读,更多相关<经典滤波算法及C语言程序(9页珍藏版)>请在人人文库网上搜索. 1.经典的滤波算法经典的滤波算法 可以用用可以用用 ...

  9. 我用纯C语言开发的中英文混合分词服务器3.0正式发布,词库190多万词,每秒切分5万+,同时提供 c、java、C#、delphi、js调用范例

    我用纯C语言开发的中英文混合分词服务器3.0正式发布,词库190多万词,每秒切分5万+,同时提供 c.java.C#.delphi.js调用范例 百万商业圈中英文混合分词服务器3.0正式发布, 绝对稳 ...

最新文章

  1. 真正的博士是如何参加AAAI, ICML, ICLR等AI顶会的?
  2. ADS与RealView MDK
  3. ASP.NET缓存之 没有为 SQL 缓存通知启用数据库“ReplaceShop”。
  4. 小米开源文件管理器MiCodeFileExplorer-源码研究(3)-使用最多的工具类Util
  5. Moonlight已经可以下载,目前是0.6版
  6. mysql删除delete语句
  7. HDL的综合和c语言的编译区别,C语言与verilog 的区别及相互转化
  8. 漫步数理统计十九——独立随机变量
  9. html ie浏览器写入内容,js在IE下创建本地文件,并读取内容
  10. r430服务器如何用u盘做系统,DELL R430服务器U盘安装操作系统指南.docx
  11. 菲尼克斯电源模块的安装
  12. Moloch网络流量分析工具
  13. 二、概率p值检验例题(R语言)
  14. 雍正《连平州志·序》:揭秘连平起源之迷
  15. 生产者消费者模式剖析
  16. 看了这篇干货,再也不怕Mac内存不足了!
  17. 剑指Offer--约瑟夫环问题
  18. PL2303HX在Windows 10下面不装安装驱动的解决办法(Code:10)
  19. 宠辱不惊,闲看庭前花开花落
  20. 个人简历自我介绍竞聘PPT模板

热门文章

  1. 新媒体增长方法从哪里找?
  2. 抓住好资产,让你赚一辈子
  3. 什么行业适合年轻人自主创业
  4. 作为一位75后的职场宝妈
  5. 微信支付宝是如何赚钱的?
  6. 监视器(monitor)
  7. access mysql 同步,SQLServer2008 同步Access数据库
  8. 使用Microsoft数据迁移助手在Oracle数据库和SQL Server之间迁移的具体示例
  9. SQL Server查询存储–概述
  10. linux用命令行运行matlab的.mat文件