• C++中的单例设计模式在多线程中的使用


    什么是单例设计模式

    什么是单例设计模式?
    指的是,在一个程序中只能存在一个的对象。即当多次创建时,后面创建的对象都指向第一个创建的对象。

    1. 将类的构造函数私有化
    2. 定义一个私有的静态类指针
    3. 创建一个函数用于判断上面的静态类指针是否为空,为空则新创建一个,不为空则直接返回上面的静态指针
    4. 创建一个内部类对象,用于释放整个静态指针,防止内存的溢出
    #include
    using namespace std;
    
    /*
     创建单例设计模式的方法:
    	1. 将类的构造函数私有化
    	2. 定义一个私有的静态类指针
    	3. 创建一个函数用于判断上面的静态类指针是否为空,为空则新创建一个,不为空则直接返回上面的静态指针
    	4. 创建一个内部类对象,用于释放整个静态指针,防止内存的溢出
    
    */
    class SignalCase
    {
    private:
    	SignalCase() {};
    private:
    	static SignalCase* signalInstance;
    public:
    	static SignalCase* getInstance()
    	{
    		if (signalInstance == NULL)
    		{
    			signalInstance = new SignalCase();
    			static inClass cl;
    		}
    		return signalInstance;
    	}
    	class inClass
    	{
    	public:
    		~inClass()
    		{
    			if (signalInstance != NULL)
    			{
    				delete SignalCase::signalInstance;
    				SignalCase::signalInstance = NULL;
    			}
    		}
    	};
    
    };
    SignalCase* SignalCase::signalInstance = NULL;
    int main()
    {
    	SignalCase* c1 = SignalCase::getInstance();
    	SignalCase* c2 = SignalCase::getInstance();
    	cout << c1 << endl;
    	cout << c2 << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    在这里插入图片描述
    c1和c2的地址是一样的,即说明其指向的是同一个对象

    单例对象在并发下会发生错误?

    static SignalCase* getInstance()
    	{
    		if (signalInstance == NULL)
    		{
    			signalInstance = new SignalCase();
    			static inClass cl;
    		}
    		return signalInstance;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    针对于这段代码

    • 当线程1执行到if语句中,判断为静态成员变量为空,则会执行if内的语句。如果此时线程切换到线程2
    • 线程2判断静态成员变量依然为空,依然会执行if内的语句,这样就会导致一个静态成员变量被赋值两次
      会导致错误

    解决方法

    可以使用互斥量的方式

    class SignalCase
    {
    private:
    	SignalCase() {};
    private:
    	static SignalCase* signalInstance;
    	static mutex mut;
    public:
    	static SignalCase* getInstance()
    	{
    		unique_lock<mutex> cur1(mut);
    		if (signalInstance == NULL)
    		{
    			signalInstance = new SignalCase();
    			static inClass cl;
    		}
    		return signalInstance;
    	}
    	class inClass
    	{
    	public:
    		~inClass()
    		{
    			if (signalInstance != NULL)
    			{
    				delete SignalCase::signalInstance;
    				SignalCase::signalInstance = NULL;
    			}
    		}
    	};
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    但是这里会导致时间的浪费,在每一次线程执行到getInstance时都要加锁,然后再判断。如果对于确定静态成员变量为空的线程这就是不必要的时间浪费
    如何解决呢?

    解决方法

    可以使用二重判断的方式,即在外面再添加一个if判断,当确定不为空时不会再有加锁和解锁的操作了

    static SignalCase* getInstance()
    	{
    		if (signalInstance == NULL) {
    			unique_lock<mutex> cur1(mut);
    			if (signalInstance == NULL)
    			{
    				signalInstance = new SignalCase();
    				static inClass cl;
    			}
    		}
    		return signalInstance;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    call_once()

    std::call_once()也可以保证函数只执行一次,用在这里也是可以的

    class SignalCase
    {
    private:
    	SignalCase() {};
    private:
    	static SignalCase* signalInstance;
    	static mutex mut;
    	static once_flag flag;
    public:
    	static void createInstance()
    	{
    		signalInstance = new SignalCase();
    		static inClass c1;
    	}
    	static SignalCase* getInstance()
    	{
    		call_once(flag, createInstance);
    		return signalInstance;
    	}
    	class inClass
    	{
    	public:
    		~inClass()
    		{
    			if (signalInstance != NULL)
    			{
    				delete SignalCase::signalInstance;
    				SignalCase::signalInstance = NULL;
    			}
    		}
    	};
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    这里可能会有疑问

    • 如果两个线程都执行到了call_once()这条语句,那不还是会导致flag不能确定吗?

    事实并不是这样,call_once再执行时会默认加一个锁,多线程执行到该条语句时要等待其他线程执行完毕之后才能执行,这样创建的once_flag就已经发生改变了,即不会再次执行call_once内定于的函数了

  • 相关阅读:
    vscode运行Live Server报错:Windows找不到文件Microsoft Edge
    关于需求规范和需求评审的一点看法
    六零导航页(LyLme Spage)导航网站源码
    基于web的酒店客房管理系统
    第二篇------Virtual I/O Device (VIRTIO) Version 1.1
    97 交错字符串
    Android AMS——栈管理基础(九)
    【RabbitMQ】SpringBoot整合RabbitMQ实现延时队列
    外汇天眼:美国10月份核心PCE物价低于预期!初请人数下降,美联储可以大胆放缓加息
    【verilog 设计】 reg有没有必要全部赋初值?
  • 原文地址:https://blog.csdn.net/m0_56104219/article/details/126910208