zoukankan      html  css  js  c++  java
  • C++ 单例模式(懒汉、饿汉模式)

    1、简单的单例模式实现

    2、C++的构造函数不是线程安全的,所以上述代码在多线程的情况下是不安全的,原因是new Singelton时,这句话不是原子的,比如一个线程执行了new的同时,另一个线程对if进行判断(此时实例还没被创建出来)。在windows下模拟:

    #include <iostream>
    #include <process.h>
    #include <windows.h>
    using namespace std;
    
    class Singelton{
    private:
        Singelton(){
    		m_count ++;
    		printf("Singelton begin
    ");
    		Sleep(1000);							// 加sleep为了放大效果
    		printf("Singelton end
    ");
    	}
        static Singelton *single;
    public:
        static Singelton *GetSingelton();
    	static void print();
    	static int m_count;
    };
    
    Singelton *Singelton::single = nullptr;
    int Singelton::m_count = 0;
    
    Singelton *Singelton::GetSingelton(){
        if(single == nullptr){
            single = new Singelton;
        }
        return single;
    }
    
    void Singelton::print(){
    	cout<<m_count<<endl;
    }
    // 回调函数
    void threadFunc(void *p){
        DWORD id = GetCurrentThreadId();		// 获得线程id
    	 cout<<id<<endl;
    	Singelton::GetSingelton()->print();		// 构造函数并获得实例,调用静态成员函数
    }
    
    int main(int argc, const char * argv[]) {
        int threadNum = 3;
        HANDLE threadHdl[100];
        
        // 创建3个线程
        for(int i = 0; i<threadNum; i++){
            threadHdl[i] = (HANDLE)_beginthread(threadFunc, 0, nullptr);
        }
        
        // 让主进程等待所有的线程结束后再退出
        for(int i = 0; i<threadNum; i++){
            WaitForSingleObject(threadHdl[i], INFINITE);
        }
    	cout<<"main"<<endl;					// 验证主进程是否是最后退出
        return 0;
    }
    

      运行结果:

    该单例模式也称为懒汉式单例。

    懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化。与之对应的是饿汉式单例。(注意,懒汉本身是线程不安全的,如上例子)

    饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。(本身就是线程安全的,如下例子)

    关于如何选择懒汉和饿汉模式:

    特点与选择:

      懒汉:在访问量较小时,采用懒汉实现。这是以时间换空间。

      饿汉:由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。

    3、饿汉式的单例实现

    #include <iostream>
    #include <process.h>
    #include <windows.h>
    using namespace std;
    
    class Singelton{
    private:
        Singelton(){
    		m_count ++;
    		printf("Singelton begin
    ");
    		Sleep(1000);							// 加sleep为了放大效果
    		printf("Singelton end
    ");
    	}
        static Singelton *single;
    public:
        static Singelton *GetSingelton();
    	static void print();
    	static int m_count;
    };
    // 饿汉模式的关键:初始化即实例化
    Singelton *Singelton::single = new Singelton;
    int Singelton::m_count = 0;
    
    Singelton *Singelton::GetSingelton(){
        // 不再需要进行实例化
        //if(single == nullptr){
        //    single = new Singelton;
        //}
        return single;
    }
    
    void Singelton::print(){
    	cout<<m_count<<endl;
    }
    // 回调函数
    void threadFunc(void *p){
        DWORD id = GetCurrentThreadId();		// 获得线程id
    	 cout<<id<<endl;
    	Singelton::GetSingelton()->print();		// 构造函数并获得实例,调用静态成员函数
    }
    
    int main(int argc, const char * argv[]) {
        int threadNum = 3;
        HANDLE threadHdl[100];
        
        // 创建3个线程
        for(int i = 0; i<threadNum; i++){
            threadHdl[i] = (HANDLE)_beginthread(threadFunc, 0, nullptr);
        }
        
        // 让主进程等待所有的线程结束后再退出
        for(int i = 0; i<threadNum; i++){
            WaitForSingleObject(threadHdl[i], INFINITE);
        }
    	cout<<"main"<<endl;					// 验证主进程是否是最后退出
        return 0;
    }
    

      运行结果:

    4、线程安全的懒汉式单例的实现

    饿汉式会提前浪费我们的内存空间以及资源,如果有项目中要求我们在使用到实例的时候再去实例化,则还是需要使用懒汉式。

    class singleton
    {
    protected:
        singleton()
        {
            // 初始化
            pthread_mutex_init(&mutex);
        }
    private:
        static singleton* p;
    public:
        static pthread_mutex_t mutex;
        static singleton* initance();
    };
    
    pthread_mutex_t singleton::mutex;
    singleton* singleton::p = NULL;
    singleton* singleton::initance()
    {
        if (p == NULL)
        {
            // 加锁
            pthread_mutex_lock(&mutex);
            if (p == NULL)
                p = new singleton();
            pthread_mutex_unlock(&mutex);
        }
        return p;
    }    

      需要注意的是:上面进行的两次if(p == NULL)的检查,因为当获得了实例之后,有了外层的判断之后,就不会再进入到内层判断,即不会再进行lock以及unlock的操作。

  • 相关阅读:
    day 66 ORM django 简介
    day 65 HTTP协议 Web框架的原理 服务器程序和应用程序
    jQuery的事件绑定和解绑 事件委托 轮播实现 jQuery的ajax jQuery补充
    background 超链接导航栏案例 定位
    继承性和层叠性 权重 盒模型 padding(内边距) border(边框) margin 标准文档流 块级元素和行内元素
    属性选择器 伪类选择器 伪元素选择器 浮动
    css的导入方式 基础选择器 高级选择器
    03-body标签中相关标签
    Java使用内存映射实现大文件的上传
    正则表达式
  • 原文地址:https://www.cnblogs.com/xuelisheng/p/9744301.html
Copyright © 2011-2022 走看看