手撕单例模式(单线程与多线程实现)

1、单线程中的单例模式

class Singleton
{
public:
    Singleton* Getinstance()
    {
        if (m_instance == nullptr)//判断是否为空
        {
            m_instance = new Singleton();//实例化一个对象
    
            std::atexit(Destructor);//保证程序退出时对象正常析构
        }
        return m_instance;//返回
    }
private:
    //私有化各类构造函数
    Singleton(){}//普通构造
    Singleton(const Singleton& s){}//拷贝构造
    Singleton& operator= (const Singleton& s){}//赋值构造
    
    ~Singleton(){}//私有化析构函数,防止被外部调用
private:
    static void Destructor()//析构回调函数
    {
        if (m_instance != nullptr)
        {
            delete m_instance;
            m_instance = nullptr;
        }
    }
    
private:
    static Singleton* m_instance;//声明静态实例
};

Singleton* Singleton::m_instance = nullptr;//初始化静态实例

2、多线程环境下的单例模式

 实现方式1:

该方式在多线程获取实例时,直接采用加互斥锁的方式,防止其他线程同时访问空的实例,导致实例被new多份。

class Singleton_MultiThread
{
public:
    Singleton_MultiThread* Getinstance()
    {
        std::lock_guard<std::mutex> lock(m_mtx);//直接在判断前加锁,加锁粒度大,性能较差
        if (m_instance == nullptr)//判断是否为空
        {
            m_instance = new Singleton_MultiThread();//实例化一个对象
            
            std::atexit(Destructor);//保证程序退出时对象正常析构
        }
        return m_instance;//返回
    }
private:
    //私有化各类构造函数
    Singleton_MultiThread(){}//普通构造
    Singleton_MultiThread(const Singleton_MultiThread& s){}//拷贝构造
    Singleton_MultiThread& operator= (const Singleton_MultiThread& s){}//赋值构造
    
    ~Singleton_MultiThread(){}//私有化析构函数,防止被外部调用
private:
    static void Destructor()//析构回调函数
    {
        if (m_instance != nullptr)
        {
            delete m_instance;
            m_instance = nullptr;
        }
    }
    
private:
    static Singleton_MultiThread* m_instance;//声明静态实例
    static std::mutex             m_mtx;
};

Singleton_MultiThread* Singleton_MultiThread::m_instance = nullptr;
std::mutex  Singleton_MultiThread::m_mtx;

实现方式2:

由于方式1每次获取都需要加锁,多线程同时访问时导致线程等待,造成性能问题。由于多线程环境下,我们只需要在实例为空时new一次就好,不需要每次都进行加锁,为此采用方式2。由于CPU或编译器会对代码进行指令重排或优化,由此加入了内存屏障和原子操作,防止因为编译器或者CPU的指令重排导致获取实例有问题,保障程序执行顺序是按照我们预期的代码顺序进行执行,同时提升了多线程访问的性能。

class Singleton_opt
{
public:
    static Singleton_opt* Getinstance()
    {
        //加入原子操作和内存屏障,防止CPU的指令优化(指令重排)而导致获取的实例对象有问题
        //std::lock_guard<std::mutex> lock(m_mtx);//直接在判断前加锁,加锁粒度大,性能较差
        Singleton_opt* tmp = m_instance.load(std::memory_order_relaxed);//取得原子变量
        std::atomic_thread_fence(std::memory_order_acquire);//内存屏障,保证获取tmp的值不被CPU优化到后面执行
        if (tmp == nullptr)//判断是否为空
        {
            std::lock_guard<std::mutex> lock(m_mtx);
            tmp = m_instance.load(std::memory_order_relaxed);
            if (tmp == nullptr)
            {
                tmp = new Singleton_opt();//实例化一个对象
                std::atomic_thread_fence(std::memory_order_release);//释放内存屏障
                m_instance.store(tmp, std::memory_order_relaxed);//将tmp的值存入m_instance
                std::atexit(Destructor);//保证程序退出时对象正常析构
            }
        }
        return tmp;//返回
    }
private:
    //私有化各类构造函数
    Singleton_opt(){}//普通构造
    Singleton_opt(const Singleton_opt& s){}//拷贝构造
    Singleton_opt& operator= (const Singleton_opt& s){}//赋值构造
    
    ~Singleton_opt(){}//私有化析构函数,防止被外部调用
private:
    static void Destructor()//析构回调函数
    {
        Singleton_opt* tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp != nullptr)
        {
            delete tmp;
            tmp = nullptr;
            m_instance.store(tmp, std::memory_order_relaxed);           
        }
    }
    
private:
    static std::atomic<Singleton_opt*> m_instance;//声明静态实例
    static std::mutex             m_mtx;
};

std::atomic<Singleton_opt*> Singleton_opt::m_instance;
std::mutex Singleton_opt::m_mtx;

 以上,是单线程和多线程环境下的单例模式,请读者批评指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值