【设计模式】单例模式 Singleton Pattern

单例模式,或称作单件模式,在整个应用程序运行中只有一个实例并提供一个全局访问点。

用途:整个程序只需要一个实例,eg.线程池、缓存、注册表、日志对象、打印机驱动等。

如何实现单一实例,

1)定义构造函数为private,禁止外部构造实例。

2)提供static的Instance函数自己提供的实例。之所以定义为static是因为构造函数为private不能通过外部创建的实例访问,只能通过静态类方法调用Singleton::Instance()。而static本身也符合唯一实例的思想。

C++实现1:

Singleton.h

#ifndef _SINGLETON_H_
#define _SINGLETON_H_
#include <iostream>

class Singleton
{
public:
    static Singleton* Instance();
private:
    Singleton();
    static Singleton* _instance;
};
#endif
View Code

Singleton.cpp

#include "Singleton.h"
#include "stdio.h"

Singleton* Singleton::_instance = NULL;

Singleton* Singleton::Instance()
{
    if (_instance == NULL)
    {
        _instance = new Singleton();
    }
    return _instance;
}

Singleton::Singleton()
{
    //todo something
}
View Code

main.cpp

#include "Singleton.h"

int main(int argc,char* argv[])
{
    Singleton* instance = Singleton::Instance();
    //todo something
    return 0;
}
View Code

C++实现1问题:以上代码未考虑多线程:

1)线程1线程2先后进入函数走到(几乎是同时)

if (instance == NULL)

2)线程1创建实例退出函数。

3)线程2已经通过instance == NULL的判断,也创建实例。

此时就创建出了2个实例,这明显违背了单一实例的原则。

C++实现2:Lazy的实现

Singleton.h

#ifndef _SINGLETON_H_
#define _SINGLETON_H_
#include <iostream>

class Singleton
{
public:
    static Singleton* Instance();
private:
    Singleton();
    static Singleton* _instance;
};
#endif
View Code

Singleton.cpp

#include "Singleton.h"
#include "stdio.h"

Singleton* Singleton::_instance = new Singleton();

Singleton* Singleton::Instance()
{
    return _instance;
}

Singleton::Singleton()
{
    //todo something
}
View Code

C++实现2问题:

此种实现在进程启动时就创建出单一实例,后面不判断直接使用。但是如果Singleton存储数据很多,而应用场景很少,比如系统从开始到结束从来被调用过单例,那么这种不管三七二十一就创建的方式对系统资源是一个浪费。

C++实现3:考虑多线程

Singleton.cpp

#include "Singleton.h"
#include "stdio.h"

Singleton* Singleton::_instance = NULL;

Singleton* Singleton::Instance()
{
    if (_instance == NULL)
    {
        mutex; //加锁-伪代码
            if (_instance == NULL)
            {
                _instance = new Singleton();
            }
        mutex; //放锁-伪代码
    }
    return _instance;
}

Singleton::Singleton()
{
    //todo something
}
View Code

为什么要2次判空。

如果一个加锁,一个判空:

1)先加锁后判空,则每次调用Singleton::Instance()都要加锁,而只有第一次创建实例的时候才有可能重复创建实例,以后每次都加锁大大浪费性能。

2)先判空后加锁,与之前没有锁是一样的,达不到防止重入的效果。

需要注意的是,访问(增删查)单例类的数据时依然需要加锁。

因为可能存在增删查同时进行的情况,而C++是通过迭代器访问数据,所以例如在线程1读的时候,线程2对数据进行了增删,则线程1的迭代器一经失效,可能因为程序崩溃。

C#实现请参考汤姆大叔的神作:

大叔手记(10):别再让面试官问你单例(暨6种实现方式让你堵住面试官的嘴)

标准版实现:

 1     public sealed class Singleton
 2     {
 3         // 依然是静态自动hold实例
 4         private static volatile Singleton instance = null;
 5         // Lock对象,线程安全所用
 6         private static object syncRoot = new Object();
 7 
 8         private Singleton() { }
 9 
10         public static Singleton Instance
11         {
12             get
13             {
14                 if (instance == null)
15                 {
16                     lock (syncRoot)
17                     {
18                         if (instance == null)
19                             instance = new Singleton();
20                     }
21                 }
22 
23                 return instance;
24             }
25         }
26     }
View Code
原文地址:https://www.cnblogs.com/TonyZhao/p/3469207.html