作者:TerryLee 创建于:2005-12-09 出处:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html 收录于:2013-03-01
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1.简单实现
1 using System; 2 class Singleton 3 { 4 private static Singleton instance; 5 protected Singleton() { } 6 public static Singleton Instance() 7 { 8 if (instance == null) 9 instance = new Singleton(); 10 return instance; 11 } 12 } 13 public class Client 14 { 15 public static void Main() 16 { 17 Singleton s1 = Singleton.Instance(); 18 Singleton s2 = Singleton.Instance(); 19 if (s1 == s2) 20 Console.WriteLine("The same instance"); 21 Console.Read(); 22 } 23 }
结果:The same instance
也可以用属性去的实例,看以下代码(C#类成员默认修饰符private):
1 public sealed class Singleton 2 { 3 static Singleton instance=null; 4 Singleton() 5 { 6 } 7 public static Singleton Instance 8 { 9 get 10 { 11 if (instance==null) 12 { 13 instance = new Singleton(); 14 } 15 return instance; 16 } 17 } 18 } 19 20 public class Client 21 { 22 public static void Main() 23 { 24 Singleton s1 = Singleton.Instance; 25 Singleton s2 = Singleton.Instance; 26 if (s1 == s2) 27 Console.WriteLine("The same instance"); 28 Console.Read(); 29 } 30 }
这种方式的实现对于线程来说并不是安全的,因为在多线程的环境下有可能得到Singleton类的多个实例。如果同时有两个线程去判断(instance == null),并且得到的结果为真,这时两个线程都会创建类Singleton的实例,这样就违背了Singleton模式的原则。
1 using System; 2 public sealed class Singleton 3 { 4 static Singleton instance=null; 5 static readonly object padlock = new object(); 6 Singleton() 7 { 8 } 9 public static Singleton Instance 10 { 11 get 12 { 13 lock (padlock) 14 { 15 if (instance==null) 16 { 17 instance = new Singleton(); 18 } 19 return instance; 20 } 21 } 22 } 23 }
这种方式的实现对于线程来说是安全的。我们首先创建了一个进程辅助对象,线程在进入时先对辅助对象加锁然后再检测对象是否被创建,这样可以确保只有一个实例被创建,因为在同一个时刻加了锁的那部分程序只有一个线程可以进入。这种情况下,对象实例由最先进入的那个线程创建,后来的线程在进入时(instence == null)为假,不会再去创建对象实例了。但是这种实现方式增加了额外的开销,损失了性能。
但是无论instance是否为空都加锁,影响性能,在lock前加一条判断
if (instance==null)
1 using System; 2 public sealed class Singleton 3 { 4 static readonly Singleton instance=new Singleton(); 5 Singleton() 6 { 7 } 8 public static Singleton Instance 9 { 10 get 11 { 12 return instance; 13 } 14 } 15 }
在此实现中,将在第一次引用类的任何成员时创建实例。
这种方法唯一的潜在缺点是,您对实例化机制的控制权较少。
1 using System; 2 public sealed class Singleton 3 { 4 internal Singleton()//注意:这里加了internal修饰,否则无法创建 5 { 6 } 7 public static Singleton Instance 8 { 9 get 10 { 11 return Nested.instance; 12 } 13 } 14 15 class Nested 16 { 17 static Nested() 18 { 19 } 20 internal static readonly Singleton instance = new Singleton(); 21 } 22 }
1 Singleton模式是限制而不是改进类的创建。
2 Singleton类中的实例构造器可以设置为Protected以允许子类派生。
3 Singleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背。
4 Singleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Singleton模式的初衷违背。
5 Singleton只考虑了对象创建的管理,没有考虑到销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。
6 理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。
7 可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。
1 实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例
2 灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题,上面的五种实现方式中已经说过了。
using System; using System.Text; using System.Threading; namespace SigletonPattern.SigletonCounter { /**//// <summary> /// 功能:简单计数器的单件模式 /// </summary> public class CountSigleton { static CountSigleton uniCounter = new CountSigleton(); /**////存储计数值 private int totNum = 0; private CountSigleton() { /**////线程延迟2000毫秒 Thread.Sleep(2000); } static public CountSigleton Instance() { return uniCounter; } /**////计数加1 public void Add() { totNum ++; } /**////获得当前计数值 public int GetCounter() { return totNum; } } //// <summary> /// 功能:创建一个多线程计数的类 /// </summary> public class CountMutilThread { static readonly object lockpad = new object(); public CountMutilThread() { } /**//// <summary> /// 线程工作 /// </summary> public static void DoSomeWork() { /**////构造显示字符串 string results = ""; /**////创建一个Sigleton实例 CountSigleton MyCounter = CountSigleton.Instance(); /**////循环调用四次 for(int i=1;i<5;i++) { lock (lockpad)//必须加上,否则这段代码没有线程安全性。 { /**////开始计数 MyCounter.Add(); results +="线程"; results += Thread.CurrentThread.Name.ToString() + "——〉"; results += "当前的计数:"; results += MyCounter.GetCounter().ToString(); results += "\n"; Console.WriteLine(results); /**////清空显示字符串 results = ""; } } } public void StartMain() { Thread thread0 = Thread.CurrentThread; thread0.Name = "Thread 0"; Thread thread1 =new Thread(new ThreadStart(DoSomeWork)); thread1.Name = "Thread 1"; Thread thread2 =new Thread(new ThreadStart(DoSomeWork)); thread2.Name = "Thread 2"; thread1.Start(); thread2.Start(); /**////线程0也只执行和其他线程相同的工作 DoSomeWork(); } } /// <summary> /// 功能:实现多线程计数器的客户端 /// </summary> public class CountClient { public static void Main(string[] args) { CountMutilThread cmt = new CountMutilThread(); cmt.StartMain(); Console.ReadLine(); } } }
结果:
说明:这里线程顺序无所谓,我们可以看到计数器是递增的,没有因为线程的不同而改变,因此,可以得知只创建了一个实例。
【1】http://terrylee.cnblogs.com/archive/2005/12/09/293509.html
【2】http://www.cnblogs.com/zhenyulu/articles/37246.html