美文网首页
1.单例模式

1.单例模式

作者: celusing | 来源:发表于2020-11-24 11:03 被阅读0次

    https://www.cnblogs.com/sunchaothu/p/10389842.html

    1.懒汉式

    #include <iostream>
    // version1:
    // with problems below:
    // 1. thread is not safe
    // 2. memory leak
    
    class Singleton{
    private:
        Singleton(){
            std::cout<<"constructor called!"<<std::endl;
        }
        Singleton(Singleton&)=delete;
        Singleton& operator=(const Singleton&)=delete;
        static Singleton* m_instance_ptr;
    public:
        ~Singleton(){
            std::cout<<"destructor called!"<<std::endl;
        }
        static Singleton* get_instance(){
            if(m_instance_ptr==nullptr){
                  m_instance_ptr = new Singleton;
            }
            return m_instance_ptr;
        }
        void use() const { std::cout << "in use" << std::endl; }
    };
    
    Singleton* Singleton::m_instance_ptr = nullptr;
    
    int main(){
        Singleton* instance = Singleton::get_instance();
        Singleton* instance_2 = Singleton::get_instance();
        return 0;
    }
    

    缺点:

    • 线程安全问题,当多线程获取单例时有可能引发竞态条件:
      第一个线程在if中判断 m_instance_ptr是空的,于是开始实例化单例;同时第2个线程也尝试获取单例,这个时候判断m_instance_ptr还是空的,于是也开始实例化单例;这样就会实例化出两个对象,这就是线程安全问题的由来;
      解决办法:加锁
    • 内存泄露:注意到类中只负责new出对象,却没有负责delete对象,因此只有构造函数被调用,析构函数却没有被调用;因此会导致内存泄漏。
      解决办法: 使用共享指针;

    2.改进版懒汉式

    #include <iostream>
    #include <memory> // shared_ptr
    #include <mutex>  // mutex
    
    // version 2:
    // with problems below fixed:
    // 1. thread is safe now
    // 2. memory doesn't leak
    
    class Singleton{
    public:
        typedef std::shared_ptr<Singleton> Ptr;
        ~Singleton(){
            std::cout<<"destructor called!"<<std::endl;
        }
        Singleton(Singleton&)=delete;
        Singleton& operator=(const Singleton&)=delete;
        static Ptr get_instance(){
    
            // "double checked lock"
            if(m_instance_ptr==nullptr){
                std::lock_guard<std::mutex> lk(m_mutex);
                if(m_instance_ptr == nullptr){
                  m_instance_ptr = std::shared_ptr<Singleton>(new Singleton);
                }
            }
            return m_instance_ptr;
        }
    
    
    private:
        Singleton(){
            std::cout<<"constructor called!"<<std::endl;
        }
        static Ptr m_instance_ptr;
        static std::mutex m_mutex;
    };
    
    // initialization static variables out of class
    Singleton::Ptr Singleton::m_instance_ptr = nullptr;
    std::mutex Singleton::m_mutex;
    
    int main(){
        Singleton::Ptr instance = Singleton::get_instance();
        Singleton::Ptr instance2 = Singleton::get_instance();
        return 0;
    }
    

    优点:

    • shared_ptr:基于RALL思想,用对象管理资源。当shared_ptr析构时,new出来的对象也会被析构掉。
    • double check:双检锁,好处是:使用锁保证了线程安全;双检:保证只有当智能指针为空的时候,才会加锁检查。避免每次调用 get_instance的方法都加锁,锁的开销毕竟还是有点大的。

    缺点:

    • 代码略显复杂,而且要求使用智能指针;
    • double check:某些平台会存在失效问题

    3.最推荐的懒汉式(magic static)-局部静态变量

    #include <iostream>
    
    class Singleton
    {
    public:
        ~Singleton(){
            std::cout<<"destructor called!"<<std::endl;
        }
        Singleton(const Singleton&)=delete;
        Singleton& operator=(const Singleton&)=delete;
        static Singleton& get_instance(){
            static Singleton instance;
            return instance;
    
        }
    private:
        Singleton(){
            std::cout<<"constructor called!"<<std::endl;
        }
    };
    

    主要利用到c++ 11的static的一个特性:

    If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
    如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。
    

    这样保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性。

    相关文章

      网友评论

          本文标题:1.单例模式

          本文链接:https://www.haomeiwen.com/subject/rrkdiktx.html