C# 单例模式

Posted ArchieCMS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 单例模式相关的知识,希望对你有一定的参考价值。

它的主要特点不是根据客户程序调用生成一个新的实例,而是控制某个类型的实例数量-唯一一个。(《设计模式-基于C#的工程化实现及扩展》,王翔)。也就是说,单例模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点。

第一种最简单,但没有考虑线程安全,在多线程时可能会出问题.

public class Singleton
{        private static Singleton instance;        private Singleton()
        {
        
        }        public static Singleton GetInstance()
        {                if(instance==null)
                {
                        instance=new Singleton();
                }                return instance;
        }
}

C# 单例模式

解析如下:

  1)首先,该Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单例的目的;

  2)因为静态变量的生命周期跟整个应用程序的生命周期是一样的,所以可以定义一个私有的静态全局变量instance来保存该类的唯一实例;

  3)必须提供一个全局函数访问获得该实例,并且在该函数提供控制实例数量的功能,即通过if语句判断instance是否已被实例化,如果没有则可以同new()创建一个实例;否则,直接向客户返回一个实例。

  在这种经典模式下,没有考虑线程并发获取实例问题,即可能出现两个线程同时获取instance实例,且此时其为null时,就会出现两个线程分别创建了instance,违反了单例规则。因此,需对上面代码修改。

第二种为多线程下的单例模式,考虑了线程安全

C# 单例模式

public class Singleton
{       private static Singleton instance;       private static object _lock=new object();       private Singleton()
       {

       }       public static Singleton GetInstance()
       {               if(instance==null)
               {                      lock(_lock)
                      {                             if(instance==null)
                             {
                                     instance=new Singleton();
                             }
                      }
               }               return instance;
       }
}

C# 单例模式

上述代码使用了双重锁方式较好地解决了多线程下的单例模式实现。先看内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。再看外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开销。

第三种饿汉模式

这种模式的特点是自己主动实例。

C# 单例模式

public sealed class Singleton
{        private static readonly Singleton instance=new Singleton(); 
        private Singleton()
        {
        }        public static Singleton GetInstance()
        {               return instance;
        }
}

C# 单例模式

上面使用的readonly关键可以跟static一起使用,用于指定该常量是类别级的,它的初始化交由静态构造函数实现,并可以在运行时编译。在这种模式下,无需自己解决线程安全性问题,CLR会给我们解决。由此可以看到这个类被加载时,会自动实例化这个类,而不用在第一次调用GetInstance()后才实例化出唯一的单例对象。

什么是单例模式?

这里我就不做过多的解释了, 毕竟关于Singleton的资料实在是太多太多了。点击这里

 

1.简单的思路就是, 创建对象单例的动作转移到另外的行为上面, 利用一个行为去创建对象自身, 如下: 

C# 单例模式

   public class Singleton
    {        private static Singleton _Singleton = null;        public static Singleton CreateInstance()
        {            if (_Singleton == null)
            {
            Console.WriteLine("被创建");             _Singleton
= new Singleton();            }            return _Singleton;        }    }

C# 单例模式

这样写看上去是没有问题, 但是有没有那种可能, 同时两个动作都判断这个对象为空, 那么这个对象就会被创建2次?是的, 多线程中, 这样是无法保证单例。

C# 单例模式

就像这样, 同时创建多个线程去创建这个对象实例的时候, 会被多次创建, 这个时候, 对代码改进一下。

C# 单例模式

    public class Singleton
    {        private static Singleton _Singleton = null;        private static object Singleton_Lock = new object(); //锁同步        public static Singleton CreateInstance()
        {                lock (Singleton_Lock)
                {
            Console.WriteLine("路过");                    
if (_Singleton == null)                    {
              Console.WriteLine("被创建");                        _Singleton
= new Singleton();                    }                }            return _Singleton;        }    }

C# 单例模式

调试代码:

C# 单例模式

               TaskFactory taskFactory = new TaskFactory();
                List<Task> taskList = new List<Task>();                for (int i = 0; i < 5; i++)
                {
                    taskList.Add(taskFactory.StartNew(() =>
                     {
                         Singleton singleton = Singleton.CreateInstance(); 
                     }));
                }

C# 单例模式

结果:

C# 单例模式

上面, 我们创建了多个线程,同时去创建这个对象的实例, 在第二次,对象命名已经被创建了, 尽管只创建了一次满足了我们的需求, 但是我们已知对象被创建了, 还需要进来做不必要的动作吗?

我们都知道, 同步锁为了达到预期的效果, 也是损耗了性能的, 那么下面的输出, 很显然是没必要的动作, 所以我们优化一下。

C# 单例模式

    public class Singleton
    {        private static Singleton _Singleton = null;        private static object Singleton_Lock = new object();        public static Singleton CreateInstance()
        {            if (_Singleton == null) //双if +lock
           {                
lock (Singleton_Lock)                {                    Console.WriteLine("路过。");                    if (_Singleton == null)                    {                        Console.WriteLine("被创建。");                        _Singleton = new Singleton();                    }                }            }            return _Singleton;        }    }

C# 单例模式

结果:

C# 单例模式

很显然, 这样达到了我们的预期, 对象在被创建后, 就没必要做多余的行为。

 

 

利用静态变量实现单例模式

C# 单例模式

    public class SingletonThird
    {        /// <summary>
        /// 静态变量
       
/// </summary> private static SingletonThird _SingletonThird = new SingletonThird();                public static SingletonThird CreateInstance()        {            return _SingletonThird;        }    }

C# 单例模式

是不是觉得很优雅, 利用静态变量去实现单例,  由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次

 

PS: 但是他的缺点也很明显, 在程序初始化后, 静态对象就被CLR构造了, 哪怕你没用。

 

 

利用静态构造函数实现单例模式

 

C# 单例模式

    public class SingletonSecond
    {        private static SingletonSecond _SingletonSecond = null;
        static SingletonSecond()
        {
                       _SingletonSecond
= new SingletonSecond();        }                public static SingletonSecond CreateInstance()        {            return _SingletonSecond;        }    }

静态构造函数:只能有一个,无参数的,程序无法调用 。

同样是由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次

 

同静态变量一样, 它会随着程序运行, 就被实例化, 同静态变量一个道理。



以上是关于C# 单例模式的主要内容,如果未能解决你的问题,请参考以下文章

线程安全的 C# 单例模式

C#中几种单例模式

C#如何实现进程单例运行

C# 单例模式

C# 单例表单模式,如何运行 2 个或更多表单? [关闭]

《C#零基础入门之百识百例》(四十五)类的属性 -- 单例模式