zoukankan      html  css  js  c++  java
  • 1.0 c#设计模式 --单例模式

    1.0 c#设计模式 --单例模式

     

    前言

          先简单说一下我前期(不理解的时候),记住单例模式的诀窍,是之前公开课上老师讲的。单例模式,双 if+lock 

     

    为什么存在单例模式?

      很多时候,一个对象我们需要使用多次。但是每次使用都要新建一次。如果实例化的时候非常耗时,那么整体的系统就会相当缓慢。

    如下代码,普通类型:

     
     /// 
        /// 学习单例模式案例
        /// 
        public class DemoSingleton
        {
            public DemoSingleton()
            {
                //模拟耗时较长的情况
                Thread.Sleep(1000);
                Console.WriteLine($"我诞生啦!在{Thread.CurrentThread.ManagedThreadId}中创建");
            }
    
            public void Test()
            {
                Console.WriteLine("DemoSingleton Test 输出");
            }
        }
    
     class Program
        {
            static void Main(string[] args)
            {
                //调用示例
                for(var i=1;i<10;i++)
                {
                    var demo = new DemoSingleton();
                    demo.Test();
                }
                Console.ReadKey();
            }
        }
     

     执行后的结果如下图:

                                                          

     根据上述执行结果我们可以看出,每次都要实例化一次类型(在我们的模拟中,实例化耗时是比较长的),所以比较浪费资源。我们应该如何改进这种方式呢?

    比较简单的就是,我们将实例化从for循环中拿出来,这样就可以实例化一次,避免了浪费时间的问题。但是,如果调用存在于多个方法中呢?

    所以,我们可以将实例化放到类中实现,代码如下:

    /// 
        /// 学习单例模式案例
        /// 
        public class DemoSingleton
        {
            private static DemoSingleton demo;
    
            private DemoSingleton()
            {
                //模拟耗时较长的情况
                Thread.Sleep(1000);
                Console.WriteLine($"我诞生啦!在{Thread.CurrentThread.ManagedThreadId}中创建");
            }
    
            public static DemoSingleton CreateInstance()
            {
                if (demo == null)
                {
                    demo = new DemoSingleton();
                }
                return demo;
            }
    
            public void Test()
            {
                Console.WriteLine("DemoSingleton Test 输出");
            }
        }
    
      static void Main(string[] args)
            {
               
                //调用示例
                for (var i = 1; i < 10; i++)
                {
                    var demo = DemoSingleton.CreateInstance();
                    demo.Test();
                }
                Console.ReadKey();
            }
      
    
    
      

    如上所示,将实例化的过程放在类中,这样不论在哪里调用,都会使用的同一个类(为了保证外部不能实例化,所以构造函数使用了私有的)。具体执行效果请看下图:

                                                 

     如上图所示,结果还是与我们想要的是一致的。但是,在程序开发过程中,通常我们会使用多线程等来处理问题,构建系统。那么调用方法就会被改成如下情况:

     
     static void Main(string[] args)
            {
                //调用示例
                for (var i = 1; i < 10; i++)
                {
                    new Action(() => {
                        var demo = DemoSingleton.CreateInstance();
                        demo.Test();
                    }).BeginInvoke(null,null);
                    
                }
                Console.ReadKey();
            }
    
     

    调用结果如下图:  

                                        

     完全不是我们想要的,但是问题出在哪里呢?

      由于我们使用的是异步方法,所以一次进入IF条件的有很多。所以,解决上面的问题就是解决如何限制一次进入IF条件的有单个。比较简单的方式,就是加一个锁,防止一次进入条件的有多个。具体代码如下:

     
        /// 
        /// 学习单例模式案例
        /// 
        public class DemoSingleton
        {
            private static DemoSingleton demo;
    
            private static readonly object _lock=new object();
    
            private DemoSingleton()
            {
                //模拟耗时较长的情况
                Thread.Sleep(1000);
                Console.WriteLine($"我诞生啦!在{Thread.CurrentThread.ManagedThreadId}中创建");
            }
    
           public static DemoSingleton CreateInstance()
            {
                if (demo == null)
                {
                    lock (_lock)
                    {
                        demo = new DemoSingleton();
                    }
                }
    
                return demo;
            }
    
            public void Test()
            {
                Console.WriteLine("DemoSingleton Test 输出");
            }
        }
    
    
    
    
            static void Main(string[] args)
            {
                //调用示例
                for (var i = 1; i < 10; i++)
                {
                    new Action(() => {
                        var demo = DemoSingleton.CreateInstance();
                        demo.Test();
                    }).BeginInvoke(null,null);
                    
                }
                Console.ReadKey();
            }
    
    
    
    
    
     

    执行效果如下图:    

                        

     如上图,不是我们想要的。这时候,有的人就说了,你的目的是让进入if条件的只有一个,应该把lock放到if的外层,那我们来修改一下构造函数:

     
      public static DemoSingleton CreateInstance()
            {
                lock(_lock)
                {
                    if (demo == null)
                    {
                        demo = new DemoSingleton();
                    }
                }
               
                return demo;
            }
    
    
     

     执行结果结果如下:   

                                                     

     如上图所示,执行结果是我们想要的了。此刻,不断进行优化的我们十分开心。但是,仔细看看代码,是不是还有优化的空间呢?我们能不能做的更好,让我们更加开心呢?

    那么,我们关注一下下面的代码,思考一下这个问题:

               lock(_lock)
                {
                    if (demo == null)
                    {
                        demo = new DemoSingleton();
                    }
                }

    我们为了让同时进入if条件的只有一个,所以添加了lock锁。那么,我们分析一下代码:i=1时,直接进入if,生成新的实例,同时i=2,i=3...被lock锁限制在外面进行等待,此刻,i=1执行结束。后面的依次进入if条件判断,发现已经存在实例了,直接返回。这样看来,是不是有些耗时呢,明明可以直接返回的,结果还在进行等待,所以我进行了以下的优化:

     
     public static DemoSingleton CreateInstance()
            {
                if(demo==null)
                {
                    lock (_lock)
                    {
                        if (demo == null)
                        {
                            demo = new DemoSingleton();
                        }
                    }
                }
                return demo;
            }

    如上代码,我们就可以在demo已经实例化的时候,无需等待直接返回。

    具体什么时候使用?

    单例模式的主要作用,是保证整个进程中对象只被实例化一次。

    正常情况下,如果一个类型,实例化的时候,非常耗时,或者计算非常复杂。就可以考虑使用单例模式,但是要注意以下的缺点

    缺点:一直占有内存(普通实例化,使用的时候创建,用完之后被CLR自动回收)

    所以,我们要根据优缺点视情况而定。而不是你一味的使用单例模式,如果电脑的内存不够大,很可能导致系统更加缓慢,或者崩溃。

    详细代码示例

      
     /// 
        /// 学习单例模式案例
        /// 
        public class DemoSingleton
        {
            private static DemoSingleton demo;
    
            private static readonly object _lock=new object();
    
            private DemoSingleton()
            {
                //模拟耗时较长的情况
                Thread.Sleep(1000);
                Console.WriteLine($"我诞生啦!在{Thread.CurrentThread.ManagedThreadId}中创建");
            }
    
            public static DemoSingleton CreateInstance()
            {
                if(demo==null)
                {
                    lock (_lock)
                    {
                        if (demo == null)
                        {
                            demo = new DemoSingleton();
                        }
                    }
                }
                return demo;
            }
    
            public void Test()
            {
                Console.WriteLine("DemoSingleton Test 输出");
            }
        } 
    
     

    其它几种单例模式的示例

    理解了上面的案例,下面的应该就更好理解了

    public class DemoSingletonStatic
        {
            private static DemoSingletonStatic demo=new DemoSingletonStatic();
    
            private DemoSingletonStatic()
            {
                //模拟耗时较长的情况
                Thread.Sleep(1000);
                Console.WriteLine($"我诞生啦!在{Thread.CurrentThread.ManagedThreadId}中创建");
            }
    
            public static DemoSingletonStatic CreateInstance()
            {
                return demo;
            }
        }
    
        public class DemoSingletonStaticTWO
        {
            private static DemoSingletonStaticTWO demo;
    
            private DemoSingletonStaticTWO()
            {
                //模拟耗时较长的情况
                Thread.Sleep(1000);
                Console.WriteLine($"我诞生啦!在{Thread.CurrentThread.ManagedThreadId}中创建");
            }
    
            static DemoSingletonStaticTWO()
            {
                demo = new DemoSingletonStaticTWO();
            }
    
           public static DemoSingletonStaticTWO CreateInstance()
            {
                return demo;
            }
    
    
    
    
    }

    遗留问题:为什么静态类不作为单例模式的经典案例呢?

    这个问题我思考了挺久,也在网上找了很多的解释,但是没有一种可以说服我。 目前有一个说法,我觉得还是相比较不错的:

      1 .我们第一种经典例子,是在使用的时候创建,而静态类是在项目启动的时候就已经创建了(但是,后面的几个静态的例子,打破了这个。这个观点只适用于第一个)

       2.静态类再使用的时候,方法不可以进行重写之类的(这个也有个疑问,我看网上有的人把单例模式中的类型上加了sealed修饰符,完全把这个有点给扼杀掉了。所以目前我也不清楚这个问题)

    这个问题,如果有看了这篇文章了解的,还希望分享一下,感谢指导!

  • 相关阅读:
    linux内核中的crng是什么?
    linux内核中的hisi_sas是什么?
    linux内核中的brd是什么?
    内核中的xenfs是什么?
    linux内核中的cfq输入输出调度算法
    linux内核中的最简单的输入输出调度算法noop
    linux内核中侧async_tx是什么?
    linux内核中的fuse是什么?
    Executors.newSingleThreadScheduledExecutor();线程池中放入多个线程问题
    Executors.newSingleThreadScheduledExecutor() 问题
  • 原文地址:https://www.cnblogs.com/kekelele/p/12367182.html
Copyright © 2011-2022 走看看