zoukankan      html  css  js  c++  java
  • 单例模式全面学习(C++版)

    单例模式:用来创建独一无二的,只能够有一个实例的对象。 单例模式的结构是设计模式中最简单的,但是想要完全实现一个线程安全的单例模式还是有很多陷阱的,所以面试的时候属于一个常见的考点~

    单例模式的应用场景:有一些对象其实只需要一个,比如:线程池,缓存,对话框,处理偏好设置和注册表的对象,日志对象,充当打印机,显卡等设备的驱动程序对象。这些对象只能够拥有一个实例,如果创建出了多个实例,就会导致一些程序的问题。程序的行为异常,资源使用的过量,或者导致不一致的结果。常用来管理共享的资源,比如数据库的连接或者线程池。

    单例模式的类图非常简单,如下~,并且经典的实现也非常的简单。

    class Singleton {
    public:
        static Singleton* getInstance();
        //析构的时候释放资源~
        ~Singleton() {
            if( (_instance != NULL) 
                delete _instance;
        }
    protected:
        Singleton();
    private:
        static Singleton* _instance;
    }
    
    Singleton *Singleton::_instance = NULL;
    Singleton* Singleton::getInstance() {
        if( _instance == NULL) {
            _instance = new Singleton();
        }
        return _instance;
    }
    

       经典的实现非常容易,但是存在一个问题,就是这个经典的实现非线程安全,多线程的情况下,这个单例模式的实现会出现问题~,如何解决呢?改进!

       

    class Lock
    {
    private:       
    	mutex mtex;
    public:
    	Lock(mutex m) : mtex(m)
    	{
    		mtex.Lock();
    	}
    	~Lock()
    	{
    		mtex.Unlock();
    	}
    };
    class Singleton {
    public:
        static Singleton* getInstance();
        //析构的时候释放资源~
        ~Singleton() {
            if( (_instance != NULL) 
                delete _instance;
        }
    protected:
        Singleton();
    private:
        static Singleton* _instance;
    static mutex m; } Singleton *Singleton::_instance = NULL; Singleton* Singleton::getInstance() {
    //check 之前进行临界区加锁操作
    Lock lock(m); if( _instance == NULL) { _instance = new Singleton(); } return _instance; }

    线程安全保证的一种方法及为在check _instance == NULL 之前进行临界区加锁,如果已经有一个线程进入访问,其他线程必须等待,这样就能够保证多线程情况下实例的唯一!

    but,互斥的同步会导致性能的降低,即使_instance已经不为空了,每次还是需要加锁,这样操作花费就比较多,性能必定比较差。

    另外还有一些比较好的方法:

    1.(非线程同步的方法)上面的操作均为一种lazy initialization的思想,及用到的时候在初始化,这样程序效率比较高,但是有一个另外比较好的方法可以采用是提前初始化,将_instance设置为static之后直接初始化为Singleton对象,每次只需要执行返回操作即可。

    这样的话同样会导致问题,就是如果单例本来资源比较多,但是不需要创建那么早,就会消耗资源~。

    class Singleton {
    public:
        static Singleton* getInstance();
        //析构的时候释放资源~
        ~Singleton() {
                delete _instance;
        }
    protected:
        Singleton();
    private:
        static Singleton* _instance;
    }
    
    Singleton *Singleton::_instance = new Singleton();
    Singleton* Singleton::getInstance() {
        return _instance;
    }
    

    2.另外一种提升因为同步导致的性能变差的方法称为“双重检验加锁”。方法如下:

    思路是只有在第一次创建的时候进行加锁,当_instance不为空的时候就不需要进行加锁的操作,这样就可以提升性能~

    class Lock
    {
    private:       
    	mutex mtex;
    public:
    	Lock(mutex m) : mtex(m)
    	{
    		mtex.Lock();
    	}
    	~Lock()
    	{
    		mtex.Unlock();
    	}
    };
    class Singleton {
    public:
        static Singleton* getInstance();
        //析构的时候释放资源~
        ~Singleton() {
            if( (_instance != NULL) 
                delete _instance;
        }
    protected:
        Singleton();
    private:
        static Singleton* _instance;
        static mutex m;
    }
    
    Singleton *Singleton::_instance = NULL;
    Singleton* Singleton::getInstance() {
        //check 之前进行临界区加锁操作
        //双重检验加锁
        if(_instance == NULL ) {
            Lock lock(m);
            if( _instance == NULL) {
                _instance = new Singleton();
            }
        }
        return _instance;
    }
    

    总之,小小单例模式问题还是挺多的,面试官喜欢问的一个问题~,因为还是有很多陷阱的。

  • 相关阅读:
    Codeforces Round #279 (Div. 2) C. Hacking Cypher 机智的前缀和处理
    Codeforces Round #279 (Div. 2) A. Team Olympiad 水题
    Codeforces Round #279 (Div. 2) B
    利用Hog特征和SVM分类器进行行人检测
    opencv 支持向量机SVM分类器
    opencv hog算子
    NOIP 2008 传纸条 NOIP 2000 方块取数 多线程DP
    POJ 1654 Area 计算几何
    hihocoder #1015 KMP
    HDU 1722 Cake 数学题
  • 原文地址:https://www.cnblogs.com/weixliu/p/3900764.html
Copyright © 2011-2022 走看看