zoukankan      html  css  js  c++  java
  • C++学习之路(四):线程安全的单例模式

    (一)简单介绍

    单例模式分为两种类型:懒汉模式和饿汉模式。

    懒汉模式:在实际类对象被调用时才会产生一个新的类实例,并在之后返回这个实例。多线程环境下,多线程可能会同时调用接口函数创建新的实例,为了防止这种并发情况,需要采用锁机制来保证线程安全性,但由于多线程抢占互斥锁,可能会导致系统的性能瓶颈。

    饿汉模式:在程序开始时就会产生一个该类的实例,并在之后返回这个实例。由于在进入主函数之前就由主线程完成了对象的初始化化,所以不用担心多线程问题,也不存在线程安全问题。

    (二)懒汉模式举例

    #include <iostream>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    using namespace std;
    
    class Singleton
    {
    private:
        Singleton()
        {
            cout<<"class create"<<endl;
        }
        static Singleton *m_instance;
        static pthread_mutex_t mutex;
        class Garbo
        {
        public:
            ~Garbo()
            {
                if (Singleton::m_instance)
                {
                    cout<<"class end"<<endl;
                    delete Singleton::m_instance;
                }
            }
        };
        static Garbo garbo;
    public:
        static Singleton *getInstance();
        // ~Singleton()
        // {
        //     cout<<"class end"<<endl;
        //     delete m_instance;
        // }
    };
    
    Singleton* Singleton::m_instance = nullptr;
    pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER;
    Singleton::Garbo Singleton::garbo;
    
    Singleton* Singleton::getInstance()
    {
        if (m_instance == nullptr)
        {
            pthread_mutex_lock(&mutex);
            if (m_instance == nullptr)
                m_instance = new Singleton();
            pthread_mutex_unlock(&mutex);
        }
       return m_instance; }
    int main(void) { Singleton *singleton = Singleton::getInstance(); return 0; }

    上述代码中,在类实例初始化时才调用getInstance创建一个新实例,为了保证线程安全(即互斥),定义互斥锁。将类构造函数定义为private,防止类外部直接调用构造函数。

    两次加锁:

    假设现在有多个线程欲创建新对象,当前m_instance为nulltpr,进入第一层if,所有线程抢占互斥锁,其中一个线程完成对类实例的创建后解锁并返回,此时唤醒其他线程,当前m_instance已被创建,其他线程将不予以创建新实例,直接返回。从这里也可以看出,由于线程之间对锁的抢占,会有一定的性能开销。

    关于单例模式的析构函数:

    由于singleton对象是一个static对象,直接delete singleton是不能够调用类析构函数的(注释内容),要想释放m_instance实例,需要内嵌一个class,并初始该静态类成员,当类成员释放时调用其析构函数从而释放m_instance实例。

    另一种写法:

    测试代码:

    class Singleton
    {
    private:
        Singleton(){}
    public:
        static Singleton* getInstance()
        {
            Lock();   //not needed after C++0X
            static Singleton instacne;
            Unlock;   //not needed after C++0X
            return instance;
        }
    }

    上述在C++0X以后,要求编译器保证内部静态变量的线程安全性,可以不加锁。但在C++0X以前,仍然需要加锁。

    (三)饿汉模式举例

    在程序开始时就会产生一个类实例,并在以后返回该实例。

    测试代码:

    class Singleton
    {
    private:
        Singleton(){}
        static Singleton *m_instance;
    public:
        static Singleton *getInstance(void)
        {
            return m_instance;
        }
    };
    
    Singleton* Singleton::m_instance = new Singleton();
  • 相关阅读:
    null和undefined的区别
    减少页面加载时间的方法
    html5有哪些新特性、移除了那些元素?
    cookies,sessionStorage 和 localStorage 的区别
    小程序页面
    快速保存网页图片的工具
    Flex 布局教程
    第一阶段:Python开发基础 day08 Python基础语法入门--列表元组字典集合类型的内置方法
    第一阶段:Python开发基础 day08 数据类型的内置方法 课后作业
    python学习第一周知识内容回顾与小结
  • 原文地址:https://www.cnblogs.com/scu-cjx/p/8602730.html
Copyright © 2011-2022 走看看