c++的单例模式的一种写法以及多线程安全问题

单例模式

Code

// 单例设计模式
class sigleC {
    
    
public:
	static sigleC* getInstance() {
    
    
		if (m_instance == nullptr) {
    
    
			m_instance = new sigleC();
			static delobj cl; // 
		}
		return m_instance;	
	}
	// 通过嵌套类来实现析构
	class delobj {
    
    
	public:
		~delobj()
		{
    
    
			if (sigleC::m_instance) {
    
    
				delete sigleC::m_instance;
				sigleC::m_instance = nullptr; // 创建的单例能一直维持到程序结束才被释放
			}
		}
	};
	void func() {
    
    
		cout << "测试" << endl;
	}
private:
	sigleC(){
    
    }
	static sigleC* m_instance;


};

// 类静态变量初始化
sigleC* sigleC::m_instance = nullptr;
int main()
{
    
    
	sigleC* p1 = sigleC::getInstance(); // 只创建一个sigleC类对象
	sigleC* p2 = sigleC::getInstance();

	return 0;
}

那么为什么不在单例类的析构函数中直接释放m_instance,而在类中嵌套另一个类?

原因

析构函数是在类的某一个对象离开作用域时自动调用的,如果在程序中创建了一个单例类对象obj1,之后obj1离开了它的作用域,后来我又创建了一个单例类对象obj2,期望是后来创建的obj2的内容和原来创建obj1时的m_instance是一样的。如果是在单例类的析构函数中释放m_instance然后置为null的话,则我后面obj2所得到的instance又是重新new出来的,和原来obj1的instance不是同一个!
总结:用老师的方式所,

多线程安全问题:双重锁定

#include <iostream>
#include <mutex>
using namespace std;
std::mutex resource_mutex;

class sigleC {
    
    
public:
	static sigleC* getInstance() {
    
    
		if (m_instance == nullptr) {
    
     // 双重锁定机制
			std::unique_lock<std::mutex> mymutex(resource_mutex);
			if (m_instance == nullptr) {
    
    
				m_instance = new sigleC();
				static delobj cl;
			}
		}
		return m_instance;
	}
	// 通过嵌套类来实现析构
	class delobj {
    
    
	public:
		~delobj()
		{
    
    
			if (sigleC::m_instance) {
    
    
				delete sigleC::m_instance;
				sigleC::m_instance = nullptr; // 创建的单例能一直维持到程序结束才被释放
			}
		}
	};
	void func() {
    
    
		cout << "测试" << endl;
	}
private:
	sigleC() {
    
    }
	static sigleC* m_instance;
};

// 线程函数
void mythread()
{
    
    
	cout << "开辟线程开始" << endl;
	sigleC* p1 = sigleC::getInstance(); // 只创建一个sigleC类对象
	cout << "开辟线程结束" << endl;
}

// 类静态变量初始化
sigleC* sigleC::m_instance = nullptr;

int main()
{
    
    
	std::thread t1(mythread);
	std::thread t2(mythread);
	t1.join();
	t2.join();
	sigleC::getInstance()->func();
	return 0;
}

双重锁定亦然存在问题,会带来reorder现象

正常的指令序列m_instance = new sigleC();

  1. 分配内存;
  2. 调用构造器
  3. 将指针返回值传递给m_instance
    我们以为cpu会这么做,但是实际上可能会发生2、3步骤交换的情况,导致双重锁定失效

解决办法如下:

在这里插入图片描述

方法2:std::call_once()

std::mutex resource_mutex;
std::once_flag g_flag; // 这是个系统定义的标记
 
class sigleC {
    
    
public:
	static void createInstance() {
    
      // 只被调用一次
		m_instance = new sigleC();
		static delobj cl;
	}

	static sigleC* getInstance() {
    
    
		std::call_once(g_flag, createInstance);  // 可以把g_flag想象成一把锁
		return m_instance;
	}
	// 通过嵌套类来实现析构
	class delobj {
    
    
	public:
		~delobj()
		{
    
    
			if (sigleC::m_instance) {
    
    
				delete sigleC::m_instance;
				sigleC::m_instance = nullptr; // 创建的单例能一直维持到程序结束才被释放
			}
		}
	};
	void func() {
    
    
		cout << "测试" << endl;
	}
private:
	sigleC() {
    
    }
	static sigleC* m_instance;
};

猜你喜欢

转载自blog.csdn.net/fly_wt/article/details/100141296
今日推荐