C#设计模式——单例模式

一、单例模式的介绍

  什么是单例模式,官方定义的是:确保一个类只有一个实例,并提供一个全局访问点

二、为什么会有单例模式

从单例模式的定义中我们可以看出,单例模式的使用自然是当我们的系统中某个对象只需要一个实例的情况,例如:操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作等。既然现实中有这样的应用场景,所以也就有了单例模式了。

三、单例模式的实现思路

 1     /// <summary>
 2     /// 单例模式实现
 3     /// </summary>
 4     public class Singleton
 5     {
 6         //定义一个静态变量保存类的实例
 7         private static Singleton singleton;
 8 
 9         /// <summary>
10         /// 定义私有构造函数
11         /// </summary>
12         private Singleton()
13         {
14         }
15 
16         /// <summary>
17         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
18         /// </summary>
19         /// <returns></returns>
20         public static Singleton GetSingleton()
21         {
22             // 如果类的实例不存在则创建,否则直接返回
23             if (singleton==null)
24             {
25                 singleton = new Singleton();
26             }
27             return singleton;
28         }
29     }

上面的单例模式在单线程的下是没问题的,但是在多线程的情况下会得到多个Singleton实例,因为两个线程在同时运行GetSingleton()方法时,此时两个线程判断(singleton==null)这个条件时返回都为真,此时两个线程就创建了两个Singleton实例,这样就违背单例模式的初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使GetSingleton()方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了,具体的解决多线程的代码如下:

 1     /// <summary>
 2     /// 单例模式实现
 3     /// </summary>
 4     public class Singleton
 5     {
 6         //定义一个静态变量保存类的实例
 7         private static Singleton singleton;
 8 
 9         // 定义一个标识确保线程同步
10         private static readonly object locker = new object();
11 
12         /// <summary>
13         /// 定义私有构造函数
14         /// </summary>
15         private Singleton()
16         {
17         }
18 
19         /// <summary>
20         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
21         /// </summary>
22         /// <returns></returns>
23         public static Singleton GetSingleton()
24         {
25             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
26             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
27             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
28             lock (locker)
29             {
30                 // 如果类的实例不存在则创建,否则直接返回
31                 if (singleton == null)
32                 {
33                     singleton = new Singleton();
34                 }
35             }
36             return singleton;
37         }
38     }

上面这种解决方案确实可以解决多线程的问题,但是上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(singleton==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(singleton==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”,下面具体看看实现代码:

 1     /// <summary>
 2     /// 单例模式实现
 3     /// </summary>
 4     public class Singleton
 5     {
 6         //定义一个静态变量保存类的实例
 7         private static Singleton singleton;
 8 
 9         // 定义一个标识确保线程同步
10         private static readonly object locker = new object();
11 
12         /// <summary>
13         /// 定义私有构造函数
14         /// </summary>
15         private Singleton()
16         {
17         }
18 
19         /// <summary>
20         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
21         /// </summary>
22         /// <returns></returns>
23         public static Singleton GetSingleton()
24         {
25             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
26             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
27             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
28             if (singleton == null)
29             {
30                 lock (locker)
31                 {
32                     // 如果类的实例不存在则创建,否则直接返回
33                     if (singleton == null)
34                     {
35                         singleton = new Singleton();
36                     }
37                 }
38             }
39             return singleton;
40         }
41     }

四、总结

到这里,设计模式的单例模式就介绍完了,希望通过本文章大家可以对单例模式有一个更深的理解。

posted @ 2020-08-18 15:50  可小宏  阅读(389)  评论(0)    收藏  举报