三种方式构建C#单例模式
标签:ons 获取 解决 inter 方法 pre 直接 经济 安全
1 ///
2 /// 双检锁实现单例
3 ///
4 public sealed class SingletonDoubleCheck
5 {
6 //s_lock对象是实现线程安全所需要的,定义这个对象时,我们假设创建单例对象的代价高于创建一个System.Object对象
7 //并假设可能根本不需要创建单例对象,否则,更经济、更简单的做法是在一个类构造器中创建单例对象
8 private static Object s_lock = new Object();
9
10 //这个字段引用一个单例对象
11 private static SingletonDoubleCheck s_value = null;
12
13 //私有构造器阻止这个类外部的任何代码创建实例
14 private SingletonDoubleCheck()
15 {
16
17 }
18
19 //以下公共静态方法返回单例对象(如有必要就创建它)
20 public static SingletonDoubleCheck GetSingleton()
21 {
22 //如果单例对象已经创建,则直接返回它
23 if (s_value != null)
24 {
25 return s_value;
26 }
27
28 //在指定对象上获取排他锁
29 Monitor.Enter(s_lock);
30
31 //再次检查是否已经创建
32 //解决问题:若多个线程首次(单例对象还未创建)同时进入,此时,只有一个线程执行下面的代码(因为有Monitor),
33 //当该线程(第一个线程)创建完实例后,另一个线程(第二个线程)立即获得锁,也执行下面的代码,
34 //此时实例已经创建完成,后面的线程应该直接返回才对,因此再判断一次实例是否已经创建。
35 if (s_value == null)
36 {
37 //若仍未创建则创建它
38 SingletonDoubleCheck singleton = new SingletonDoubleCheck();
39
40 //将singleton给s_value
41 //下面的代码保证singleton中的引用只有在构造器结束执行之后才发布到s_value中
42 Volatile.Write(ref s_value, singleton);
43
44 //注意:下面的写法是不牢靠的
45 //因为编译器可能会这样做:
46 //1.为SingletonDoubleCheck分配内存
47 //2.将引用发布到(赋给)s_value
48 //3.调用构造器
49 //假设在将引用发布给s_value之后,但在调用构造器之前,若有另一个线程调用了GetSingleton,
50 //此时s_value不为null,该线程会使用该对象,但该对象的构造器还没执行完成。
51 //s_value = new SingletonDoubleCheck();
52 }
53
54 //释放指定对象上的排他锁
55 Monitor.Exit(s_lock);
56
57 return s_value;
58 }
59 }
60
61 ///
62 /// C#下简单的构造单例方法
63 /// CLR已保证了对类的构造是线程安全的,书写非常简便
64 /// 缺点也很明显,首次访问类的任何成员时都会调用类型构造器
65 /// 所以,如果该类定义了其它静态成员,就会在访问其它任何静态成员时创建该对象
66 ///
67 public sealed class SingletonSimple
68 {
69 private static SingletonSimple s_value = new SingletonSimple();
70
71 //私有构造器阻止这个类外部的任何代码创建实例
72 private SingletonSimple()
73 {
74
75 }
76
77 //以下公共静态方法返回单例对象(如有必要就创建它)
78 public static SingletonSimple GetSingleton()
79 {
80 return s_value;
81 }
82
83 //或
84 public static SingletonSimple SingletonInstance
85 {
86 get { return s_value; }
87 }
88
89 //或
90 public static SingletonSimple Instance { get; } = new SingletonSimple();
91 }
92
93 ///
94 /// 嵌套类实现单例
95 /// 如果多个线程同时调用GetSingleton,则可能创建多个SingletonNested对象
96 /// 但由于使用了Interlocked.CompareExchange,所以保证只会有一个引用被发布到s_value
97 /// 没有被固定下来的对象都会被垃圾回收
98 /// 该种方式不会阻塞线程
99 ///
100 public sealed class SingletonNested
101 {
102 private static SingletonNested s_value = null;
103
104 //私有构造器阻止这个类外部的任何代码创建实例
105 private SingletonNested()
106 {
107
108 }
109
110 //以下公共静态方法返回单例对象(如有必要就创建它)
111 public static SingletonNested GetSingleton()
112 {
113 //如果单例对象已经创建,则直接返回它
114 if (s_value != null)
115 {
116 return s_value;
117 }
118
119 //创建一个新的单例对象,并把它固定下来(如果另一个线程还没有固定它的话)
120 SingletonNested singletonNested = new SingletonNested();
121
122 //比较两个指定的引用类型的实例 T 是否相等,如果相等,则替换第一个,并且返回s_value原始值
123 //s_value与null比较,如果相等则用singletonNested替换s_value,否则不替换
124 Interlocked.CompareExchange(ref s_value, singletonNested, null);
125
126 //如果该线程竞争失败,则新建的第二个单实例对象会被垃圾回收
127
128 return s_value;
129 }
130 }
三种方式构建C#单例模式
标签:ons 获取 解决 inter 方法 pre 直接 经济 安全
原文地址:https://www.cnblogs.com/xuejietong/p/9016890.html
评论