单例模式
需求:
写一个MDI 窗体程序,当有一个是“工具箱”的窗体,想要实现点击(可以多次点击)工具箱出现一次,不是出现多次。
private void Form1_load(object sender,EventArgs e)
{
//也可以设置Form1窗体的IsIsMdiContainter属性为true
this.IsMdiContainter=true;
}
private void ToolstripMenuItemToolbox_Click(object sender,EventArgs e)
{
//"工具箱"的启动代码,实例化FormToolBox,并设置其Mdi父窗体为Form1
FormToolBox ftb=new FormToolBox();
ftb.Mdiparent=this;
ftb.show();
}
解决办法:
判断一下,这个FormToolBox有没有实例化就可以了
按了菜单的按钮时,再去FormToolBox ftb=new FormToolBox();这样就是实例化,这样我们把声明的工作放到全局变量来完成这样就解决了。
private FormToolBox ftb;
private void ToolstripMenuItemToolbox_Click(object sender,EventArgs e)
{
//判断是否实例化,如果为null说明没有实例化
if(ftb==null)
{
ftb=new FormToolBox();
ftb.Mdiparent=this;
ftb.Show();
}
}
增加一个工具栏按钮的事件处理
//增加一个工具栏按钮的事件处理
private void toolStripButton1_Click(object sender,EventArgs e)
{
if(ftb==null)
{
ftb=new FormToolbox();
ftb.MdiParent=this;
ftb.Show();
}
}
当程序运行后,启动’工具箱’,在把’工具箱’ 窗体关闭,'工具箱’就不见了,因为它的实例没有变为null,而是disposed。所以还要增加disposed的判断
private void toolStripButton1_Click(object sender,EventArgs e)
{
if(ftb==null||ftb.Isdisposed)
{
ftb=new FormToolbox();
ftb.MdiParent=this;
ftb.Show();
}
}
单例模式:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法是,让类自己负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方方法。
单例模式结构图
| Singleton |
|---|
| -instance:Singleton() |
| -Singleton() |
| +GetInstance() |
Singleton类,定义一个GetInstance,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。
class Singleton
{
private static Singleton instance;
//构造方法让其private,这就赌死了外界利用new创建此类实例的可能
private Singleton()
{
}
//此方法是获得本类实例的唯一全局访问点
public static Singleton GetInstance()
{
//若实例不存在,则new一个新实例,否则返回已有的实例
if(instance==null)
{
instance=new Singleton();
}
return instance;
}
}
//客户端代码
static void main(string []args)
{
Singleton s1=Singleton.GetInstance();
Singleton s2=Singleton.GetInstance();
//判断两次实例化的对象是结果相同
if(s1==s2)
{
Console.WriteLine("两个对象是相同的实例。");
}
Console.read();
}
单例模式的好处:
单例模式因为Singleton类封装它的唯一实例,这样它可以严格控制客户怎样访问它以及何时访问它。简单的说就是对唯一实例的受控访问。
多线程时的单例
问题:
在多线程同时,注意是同时访问Singleton类,调用GetInstance()方法,会有可能造成多个实例。
解决办法:
把进程交给一把锁处理。lock,是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程想进入锁定的代码,则它一直等待(被阻止),直到对象被释放。
函数代码:
class Singleon
{
private static Singleton instance;
//程序运行时创建一个静态只读的进程辅助对象
private static readonly object syncroot=new object();
private Singleton(){}
public static Singleton GetInstance()
{
//在同一时刻加了锁的那部分程序只有一个线程可以进入
lock(syncroot)
{
if(instance==null)
{
instance=new Singleton();
}
}
return instance;
}
}
这段代码让最先进入的那个线程创建对象实例,以后的线程进入时不会再去创建对象实例了。由于有了lock,就保证了多线程环境下的同时访问也不*会造成多个实例的生成。
双重锁定:
class Singleon
{
private static Singleton instance;
//程序运行时创建一个静态只读的进程辅助对象
private static readonly object syncroot=new object();
private Singleton(){}
public static Singleton GetInstance()
{
//先判断实例是否存在,不存在再加锁处理
if(instance==null)
{
//在同一时刻加了锁的那部分程序只有一个线程可以进入
lock(syncroot)
{
if(instance==null)
{
instance=new Singleton();
}
}
}
return instance;
}
}
这段代码解决了不用让线程每次都加锁,而只是实例化未被创建的时候再加锁处理。同时也能保证多线程的安全。这种方法被称为双重锁定(double-check locking)。
问题:
外面已经有了判断了instance实例是否存在,为什么在lock里面还需要做一次instance实例是否存在的判断?
‘回答:
1.当instance存在的情况,就直接返回是可以的。
2.当instance为null,且同时有两个线程调用Getinstance()方法时,它们都要经过第一次instance==nul的判断。然后由于lock机制,两个线程只有一个进入,另一个在外边排队等待,必须要其中的一个进入并出来后,另一个进入。如果没有第二次的instance为null的判断,那么第一个线程创建了实例,第二个线程还是可以继续创建新的实例,这就没有实现单例。
单例模式有个两个模式:饿汉模式,懒汉模式。
1.饿汉模式:当instance变量标记readonly时,在静态初始化期间或者在类的构造函数中分配变量,这种静态初始化的方式是在自己被加载时就将自己被实例化。
2.懒汉模式:在第一次被引用时,把自己实例化。也就是,当程序第一次放访问单例模式实例时进行创建。
分析懒汉模式和饿汉模式:
饿汉式:静态初始化的方式,它是类一加载就实例化的对象,因此需要提前占用系统资源。空间换取时间
懒汉式:面临多线程访问的安全性问题,因此需要双重锁定确保安全。
3410

被折叠的 条评论
为什么被折叠?



