码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式-借还模式

时间:2015-05-16 17:47:35      阅读:96      评论:0      收藏:0      [点我收藏+]

标签:

我个人比较注重代码的性能,包括时间和空间,代码的可控度;在做开发的时候偶然发现并实现了这个方式,我个人命名他为“借还模式”。不知道是否有相关的模式,我读书少,嘿嘿。。。

很多时候,我们执行一个方法,必须要有很多的new操作,当这个方法执行次数非常多的时候,这个方法内部new的操作将是超级多的。但是我们又不能手动释放内存,只能等待GC来回收。那么我们就有一个问题,既然不能手动释放,而且反正要new,那么干脆我们就不让GC去回收了,让他持续的存在,这样还能节省内存的空间,不是一举两得吗?由此我想到图书馆的图书实现过程,图书的所有权是图书馆的,借出去的是使用权,用完了必须归还。那么就有两个关键的操作“借”和“还”,“借”和“还”都指的是使用权。

 

 

代码实现如下:

  1     /// <summary> 对象借还管理器 </summary>
  2     public class BRManage : IDisposable
  3     {
  4         private static BRManage br;
  5         private static readonly object ins = new object();
  6         private readonly object globalLock = new object();
  7         
  8         private const int WARNING_COUNT = 10;
  9         private readonly Dictionary<Type, Queue<object>> cachePool = new Dictionary<Type, Queue<object>>();
 10         private readonly Dictionary<Type, List<object>> cacheBak = new Dictionary<Type, List<object>>();
 11         
 12         private bool isdisposing;
 13 
 14         private BRManage()
 15         {
 16         }
 17 
 18         /// <summary> 单例 </summary>
 19         public static BRManage Ins
 20         {
 21             get
 22             {
 23                 if (br == null)
 24                 {
 25                     lock (ins)
 26                     {
 27                         if (br == null)
 28                         {
 29                             br = new BRManage();
 30                         }
 31                     }
 32                 }
 33 
 34                 return br;
 35             }
 36         }
 37 
 38         ///<summary> 借出一个对象 </summary>
 39         public T Borrow<T>() where T : class, new()
 40         {
 41             if (isdisposing)
 42                 throw new ApplicationException("借还管理器已经释放,不能再使用,请重启系统");
 43 
 44             var type = typeof (T);
 45 
 46             if (!cacheBak.ContainsKey(type))
 47             {
 48                 lock (globalLock)
 49                 {
 50                     if (!cacheBak.ContainsKey(type))
 51                     {
 52                         cachePool.Add(type, new Queue<object>());
 53                         cacheBak.Add(type, new List<object>());
 54                     }
 55                 }
 56             }
 57 
 58             var que = cachePool[type];
 59             var list = cacheBak[type];
 60 
 61             var bak = WARNING_COUNT - que.Count;
 62             if (bak > 0)
 63             {
 64                 for (int i = 0; i < bak; i++)
 65                 {
 66                     var temp = new T ();
 67                     list.Add(temp);
 68                     que.Enqueue(temp);
 69                 }
 70             }
 71 
 72             return que.Dequeue() as T;
 73         }
 74 
 75         ///<summary> 返还一个对象 </summary>
 76         public void Return<T>(T obj) where T : class
 77         {
 78             if (isdisposing)
 79                 throw new ApplicationException("借还管理器已经释放,不能再使用,请重启系统");
 80 
 81             if (obj == null)
 82                 throw new ArgumentNullException("obj");
 83 
 84             var type = obj.GetType();
 85             if (!cachePool.ContainsKey(type))
 86             {
 87                 throw new ArgumentNullException("obj", "该类型未注册过");
 88             }
 89 
 90             if (cacheBak[type].IndexOf(obj) == -1)
 91             {
 92                 throw new ArgumentException("该对象未被索引,不被借还管理", "obj");
 93             }
 94             
 95             cachePool[type].Enqueue(obj);
 96         }
 97 
 98         /// <summary> 释放所有数据 </summary>
 99         public void Dispose()
100         {
101             isdisposing = true;
102 
103             var list = new List<Type>(cachePool.Keys);
104             for (int i = 0, l = list.Count; i < l; i++)
105             {
106                 cachePool[list[i]].Clear();
107                 cachePool[list[i]].TrimExcess();
108             }
109             cachePool.Clear();
110             list.Clear();
111 
112             list = new List<Type>(cacheBak.Keys);
113             for (int i = 0, l = list.Count; i < l; i++)
114             {
115                 cacheBak[list[i]].Clear();
116                 cacheBak[list[i]].TrimExcess();
117             }
118             cacheBak.Clear();
119             list.Clear();
120             list.TrimExcess();
121         }
122     }

 

 

 

代码解释:

这里单例是为了方便操作,我不喜欢new,也不喜欢static。new代表着空间减少,static如果单纯的作为计算数据没问题,如果保存数据带来并发问题,避免这些问题要增加复杂度。cacheBak的作用是保存对象的所有权,作用是在Dispose的时候释放列表。当然Dispose这里比较简单,在实际用的时候可能需要调用对象的Close或者Dispose方法。cachePool是一个队列,队列是个好东西,这里用来记录对象的使用权,表示还有那些对象可供借出。这里借出对象是没有选择性的,实际根据业务可以有选择的借出。另外有一个WARNING_COUNT常量,相当于警告库存的概念,让使用权队列始终大于10个长度,这样避免并发操作Dequeue抛出操作异常的问题,当然是一定程度上。

 

 

借还模式注意事项:

  1. Borrow和Return必须成对出现,正所谓“有借有还再借不难”,否则就是人品问题;那么代码就是质量问题,如果不Return的后果可能是内存占用量急剧增长。
  2. 这里用了泛型方法和泛型约束,要求T必须是class,很显然如果是值类型,按副本传递就失去了意义;并且可以new,因为在Borrow里面需要创建对象。
  3. 在使用完毕的时候,一般系统或者程序关闭的时候执行Dispose,释放使用权和所有权。
  4. 如果需要的话,可以在Return或者Dispose的时候可以对对象进行一些操作。
  5. 对于非托管代码慎用,例如对同一个文件操作的Stream可能会出现不可以意料的异常。

 

 

借还模式的扩展:

  1. 可以增加限制让cacheBak所有权列表的长度,不要让所有权列表一直增长下去,可以限制在某个界限值,如果没有可用使用权对象,线程就Sleep一会。
  2. 可以对Type类型做限制,增加安全性,可以增加一个Register方法,对能使用的Type进行注册,而不是在Borrow中自动创建
  3. 如果Return的对象在其他地方还有保留的引用,而我又不想让这些保留的引用使用的时候,可以在Return的时候对这个对象进行一些的操作,防止其他保留的引用继续使用,当然比较麻烦。

 

 

使用场景:

  1. 大量小对象的创建,可以节省内存
  2. 珍惜资源的创建,嘿嘿,你是否想到了不关闭的Connection对象,我这么用了,目前没问题。

 

 

测试结果:

先看测试代码如下

class Program
    {
        static void Main(string[] args)
        {
            var dt = DateTime.Now;
            // A 方式
            //for (int i = 0; i < 10000000; i++)
            //{
            //    var o = new MyClass();
            //    o.p1 = 123;
            //    o.p2 = o.p1;
            //    o.p3 = 12;
            //    o.p4 = o.p1.ToString();
            //    o.p5 = Guid.NewGuid();
            //}
            //Console.WriteLine((DateTime.Now-dt).TotalMilliseconds);

            // B 方式
            dt = DateTime.Now;
            for (int i = 0; i < 10000000; i++)
            {
                var o = BRManage.Ins.Borrow<MyClass>();
                BRManage.Ins.Return(o);
            }
            Console.WriteLine((DateTime.Now - dt).TotalMilliseconds);
            Console.ReadLine();
        }
    }

    class MyClass
    {
        public int p1 { get; set; }
        public int p2 { get; set; }
        public byte p3 { get; set; }
        public string p4 { get; set; }
        public Guid p5 { get; set; }
    }
  CPU极值 执行时间(ms)  最高内存占用(m)
A方案 35% 4300-4500 3.7M
B方案 35% 4300-4500 2.1M

测试比较片面,实际应用的时候比较复杂,整个系统测试比较麻烦,个人认为总的占用空间会减少,同时性能不会降低。

 

 

综述:该模式依然是创建模式,主要功能是能节省一定的空间,例子代码中的性能和直接new的性能是一样的,测试的时候有上下浮动,如果对Borrow和Return进行其他操作的时候多多少少会影响一些性能,大家就自己斟酌吧。欢迎园友拍砖,丢鸡蛋。

设计模式-借还模式

标签:

原文地址:http://www.cnblogs.com/plutowang/p/4508025.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!