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; } }
解析如下:
1)首先,该Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单例的目的;
2)因为静态变量的生命周期跟整个应用程序的生命周期是一样的,所以可以定义一个私有的静态全局变量instance来保存该类的唯一实例;
3)必须提供一个全局函数访问获得该实例,并且在该函数提供控制实例数量的功能,即通过if语句判断instance是否已被实例化,如果没有则可以同new()创建一个实例;否则,直接向客户返回一个实例。
在这种经典模式下,没有考虑线程并发获取实例问题,即可能出现两个线程同时获取instance实例,且此时其为null时,就会出现两个线程分别创建了instance,违反了单例规则。因此,需对上面代码修改。
第二种为多线程下的单例模式,考虑了线程安全
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; } }
上述代码使用了双重锁方式较好地解决了多线程下的单例模式实现。先看内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。再看外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开销。
第三种饿汉模式
这种模式的特点是自己主动实例。
public sealed class Singleton { private static readonly Singleton instance=new Singleton(); private Singleton() { } public static Singleton GetInstance() { return instance; } }
上面使用的readonly关键可以跟static一起使用,用于指定该常量是类别级的,它的初始化交由静态构造函数实现,并可以在运行时编译。在这种模式下,无需自己解决线程安全性问题,CLR会给我们解决。由此可以看到这个类被加载时,会自动实例化这个类,而不用在第一次调用GetInstance()后才实例化出唯一的单例对象。
什么是单例模式?
这里我就不做过多的解释了, 毕竟关于Singleton的资料实在是太多太多了。点击这里
1.简单的思路就是, 创建对象单例的动作转移到另外的行为上面, 利用一个行为去创建对象自身, 如下:
public class Singleton { private static Singleton _Singleton = null; public static Singleton CreateInstance() { if (_Singleton == null) {
Console.WriteLine("被创建"); _Singleton = new Singleton(); } return _Singleton; } }
这样写看上去是没有问题, 但是有没有那种可能, 同时两个动作都判断这个对象为空, 那么这个对象就会被创建2次?是的, 多线程中, 这样是无法保证单例。
就像这样, 同时创建多个线程去创建这个对象实例的时候, 会被多次创建, 这个时候, 对代码改进一下。
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; } }
调试代码:
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(); })); }
结果:
上面, 我们创建了多个线程,同时去创建这个对象的实例, 在第二次,对象命名已经被创建了, 尽管只创建了一次满足了我们的需求, 但是我们已知对象被创建了, 还需要进来做不必要的动作吗?
我们都知道, 同步锁为了达到预期的效果, 也是损耗了性能的, 那么下面的输出, 很显然是没必要的动作, 所以我们优化一下。
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; } }
结果:
很显然, 这样达到了我们的预期, 对象在被创建后, 就没必要做多余的行为。
利用静态变量实现单例模式
public class SingletonThird { /// <summary> /// 静态变量
/// </summary> private static SingletonThird _SingletonThird = new SingletonThird(); public static SingletonThird CreateInstance() { return _SingletonThird; } }
是不是觉得很优雅, 利用静态变量去实现单例, 由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次
PS: 但是他的缺点也很明显, 在程序初始化后, 静态对象就被CLR构造了, 哪怕你没用。
利用静态构造函数实现单例模式
public class SingletonSecond { private static SingletonSecond _SingletonSecond = null; static SingletonSecond() {
_SingletonSecond = new SingletonSecond(); } public static SingletonSecond CreateInstance() { return _SingletonSecond; } }
静态构造函数:只能有一个,无参数的,程序无法调用 。
同样是由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次
同静态变量一样, 它会随着程序运行, 就被实例化, 同静态变量一个道理。
以上是关于C# 单例模式的主要内容,如果未能解决你的问题,请参考以下文章