首页 > 代码库 > 单例模式易错分析
单例模式易错分析
最近在学些设计模式,今天记录的是单例模式,单例模式在平时的工作中运用的还是比较多的,是一种常用的软件设计模式,通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式将是您的是最好的解决方案。
先上代码:
public class SigletonCounter { //static SigletonCounter instance = new SigletonCounter(); static SigletonCounter instance = null;//将类定义为静态类 public static SigletonCounter Instance { get { if (instance == null) { instance = new SigletonCounter();//实例化该类 } return instance; } } private int totnum = 0;//计数用 public void add() { totnum++; } public int GetCounter() { return totnum; } }
以上的代码就是一个简单的单例实例了,在一般的单一线程程序中,看上去是足够了,但是在多线程的情况下似乎就达不到单例的效果了,有可能出现两个线程同时判断了if (instance == null)这个方法,导致了存在两个实例,造成错误,下面我们来验证下这样的情况,上代码:
/// <summary> /// 用于显示 /// </summary> void Write() { string results = ""; SigletonCounter mycounter = SigletonCounter.Instance; for (int i = 0; i < 3; i++) { mycounter.add(); results += Thread.CurrentThread.Name.ToString() + "对应"; results += "当前的计数:"; results += mycounter.GetCounter().ToString(); results += "\n"; Console .WriteLine ( results); results = ""; } }
{
Thread thread0 = Thread.CurrentThread; thread0.Name = "线程 0"; Thread thread1 = new Thread(new ThreadStart(DOwork)); thread1.Name = "线程 1"; Thread thread2 = new Thread(new ThreadStart(DOwork)); thread2.Name = "线程 2"; Thread thread3 = new Thread(new ThreadStart(DOwork)); thread3.Name = "线程 3"; thread1.Start(); thread2.Start(); thread3.Start(); Write();
}
运行一下结果为:
线程 3对应当前的计数:1
线程 3对应当前的计数:3
线程 3对应当前的计数:4
线程 1对应当前的计数:1
线程 1对应当前的计数:2
线程 1对应当前的计数:3
线程 0对应当前的计数:5
线程 0对应当前的计数:6
线程 0对应当前的计数:7
线程 2对应当前的计数:2
线程 2对应当前的计数:8
线程 2对应当前的计数:9
从上面的结果看,和预测的结果是相符合的,其中出现了重复的数,按照单例模式的理论是不应该出现该问题的,totnum的值应该是唯一的,但是事实摆在面前,我们得去解决它。
现在对程序SigletonCounter类做改进如下:
static SigletonCounter instance = null;//将类定义为静态类 private int totnum = 0;//计数用 static readonly object padlock = new object(); public static SigletonCounter Instance { get { if (instance == null)//多加一次判断是因为,此判断在此可提升性能,少做lock操作 { lock (padlock) { if (instance == null) { instance = new SigletonCounter(); } } } return instance; } } public void add() { totnum++; } public int GetCounter() { return totnum; }
运行得到结果是:
线程 1对应当前的计数:1
线程 1对应当前的计数:2
线程 1对应当前的计数:3
线程 0对应当前的计数:4
线程 0对应当前的计数:5
线程 0对应当前的计数:6
线程 3对应当前的计数:7
线程 3对应当前的计数:8
线程 3对应当前的计数:9
线程 2对应当前的计数:10
线程 2对应当前的计数:11
线程 2对应当前的计数:12
这样的就达到我们的目的了,在同一个时刻加了锁的那部分程序只有一个线程可以进入,避免了同时判断造成的错误,
单例模式易错分析