单例
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例(eg:应对一些特殊情况,比如数据库连接池(内置了资源) 全局唯一号码生成器),才能确保它们的逻辑正确性、以及良好的效率。
优点:单例的好处就是单例,就是全局唯一的一个实例
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例
缺点:单例可以避免重复创建,但是也会常驻内存 除非是真的有必要,否则不要单例
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类
如何绕过常规的构造器,提供一种机制来保证一个类只创建一个实例?
如何实现?将构造函数私有化,然后对外提供一个公开的静态方法,使用一个静态属性进行判断当前对象是否被创建

1 // 不要用这种方式
2 public class Singleton
3 {
4 private static Singleton _instance = null;
5 private Singleton() { }
6 public static Singleton CreateInstance()
7 {
8 if (_instance == null)
9 {
10 _instance = new Singleton();
11 }
12 return _instance;
13 }
14 }
上面的方法是非线程安全的,多个不同的线程可以同时进入这个方法,如果instance为空的并且这里返回真的情况下,都可以创建实例,这显然违反了单例模式,实际上,在测试以前,实例就已经有可能被创建了,但是内存模型不能保证这个实例能被其他的线程看到,除非合适的内存屏障已经被跨过了
我们把上面的代码优化一下

1 /// <summary>
2 /// 单例类:一个构造对象很耗时耗资源类型
3 /// 懒汉式单例模式
4 /// </summary>
5 public class Singleton
6 {
7 /// <summary>
8 /// 构造函数耗时耗资源
9 /// </summary>
10 private Singleton()
11 {
12 long lResult = 0;
13 for (int i = 0; i < 10000000; i++)
14 {
15 lResult += i;
16 }
17 Thread.Sleep(2000);
18 Console.WriteLine("{0}被构造一次", this.GetType().Name);
19 }
20 /// <summary>
21 /// 3 全局唯一静态 重用这个变量
22 /// </summary>
23 private static volatile Singleton _Singleton = null;
24 //volatile 促进线程安全 让线程按顺序操作
25 private static readonly object Singleton_Lock = new object();
26 /// <summary>
27 /// 2 公开的静态方法提供对象实例
28 /// </summary>
29 /// <returns></returns>
30 public static Singleton CreateInstance()
31 {
32 if (_Singleton == null)//是_Singleton已经被初始化之后,就不要进入锁等待了
33 {
34 lock (Singleton_Lock)
35 //保证任意时刻只有一个线程进入lock范围
36 //也限制了并发,尤其是_Singleton已经被初始化之后
37 {
38 //Thread.Sleep(1000);
39 //Console.WriteLine("等待锁1s之后才继续。。。");
40 if (_Singleton == null)//保证只实例化一次
41 {
42 _Singleton = new Singleton();
43 }
44 }
45 }
46 return _Singleton;
47 }
48
49 //既然是单例,大家用的是同一个对象,用的是同一个方法,那还会并发吗 还有线程安全问题吗?
50 public int iTotal = 0;
51 public void Show()
52 {
53 //lock (Singleton_Lock)
54 //{
55 this.iTotal++;
56 //}
57 }
58
59 public static void Test()
60 {
61 Console.WriteLine("Test1");
62 Console.WriteLine(_Singleton.iTotal);
63 }
64
65 }
前端调用

1 {
2 List<Task> tasks = new List<Task>();
3 for (int i = 0; i < 10000; i++)
4 {
5 tasks.Add(Task.Run(() =>
6 {
7 Singleton singleton = Singleton.CreateInstance();
8 singleton.Show();
9 }));
10 }
11 Task.WaitAll(tasks.ToArray());
12 Singleton.Test();
13 //iTotal 是0 1 10000 还是其他的
14 //其他值,1到10000范围内都可能 线程不安全
15
16 }
