单例模式

单例模式

确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例

  • 只能有一个实例;(禁止拷贝和赋值,静态的实例对象)
  • 必须自己创建这个实例;(私有化构造函数)
  • 必须自己向整个系统提供这个实例。(静态的方法调用构造函数)
  • 多线程使用双重检查确保线程安全。
#ifndef __SINGLETON_H__
#define __SINGLETON_H__
 
#include <iostream>
#include <string.h>
#include <mutex>
using namespace std;
 
class Singleton
{
public:
    static Singleton* getInstance();
	
private:
	Singleton(){}
	Singleton(const Singleton& other) {}
	Singleton& operator=(const Singleton& other) {}

	static Singleton* m_instance;
	static std::mutex m_mutex;
};
 
Singleton* Singleton::m_instance= nullptr;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance(){
		if (m_instance== nullptr){//双重检查
			std::lock_guard<std::mutex> lock(m_mutex);
			if (m_instance== nullptr){
				m_instance= new Singleton();
			}
		}
		return m_instance;
	}
 
#endif //__SINGLETON_H__

 由于new的动作并不是原子操作,所以在多线程中,一般的单例模式会出现reorder的情况,程序会发生意想不到的错误;

编译器会对代码进行优化,编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令

 若有两个线程A、B,当线程A执行到instance = new Singleton()时,由于发生了reorder,可能先完成了步骤1和步骤3,还未执行步骤2,此时m_instance已经不是NULL,但是并未完成构造;此时线程B调用getInstance()函数时,判断m_instance并不是nullptr,直接返回线程A为构造完成的m_instace,这样线程就不是安全的,会造成程序的崩溃。

下面时c++11的解决方案:

#ifndef __SINGLETON_H__
#define __SINGLETON_H__
 
#include <iostream>
#include <string.h>
#include <mutex>
using namespace std;
//方案1
class Singleton
{
public:
	static Singleton* getInstance();
	virtual ~Singleton() {}
private:

	Singleton() {}
	Singleton(const Singleton& other) {}
	Singleton& operator=(const Singleton& other) {}

	static std::atomic<Singleton*> m_instance;
	static std::mutex m_mutex;
};

// 线程安全的双检查锁 -- C++ 11版本之后的跨平台实现
std::atomic<Singleton*> Singleton::m_instance = nullptr;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() 
{
	Singleton* tmp = m_instance.load(std::memory_order_relaxed);
	std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
	if (tmp == nullptr) {
		std::lock_guard<std::mutex> lock(m_mutex);
		tmp = m_instance.load(std::memory_order_relaxed);
		if (tmp == nullptr) {
			tmp = new Singleton();
			std::atomic_thread_fence(std::memory_order_release);//释放内存fence
			m_instance.store(tmp, std::memory_order_relaxed);
		}
	}
	return tmp;
}

//方案2,使用关键字volatile,不是跨平台
class Singleton2
{
public:
	volatile static Singleton2* getInstance();
	virtual ~Singleton2() {}
private:

	Singleton2() {}
	Singleton2(const Singleton& other) {}
	Singleton2& operator=(const Singleton2& other) {}

	volatile static Singleton2* m_instance;
	static std::mutex m_mutex;
};


std::mutex Singleton2::m_mutex;
volatile Singleton2* Singleton2::m_instance = nullptr;

volatile Singleton2* Singleton2::getInstance() {
	if (m_instance == nullptr) {
		std::lock_guard<std::mutex> lock(m_mutex);
		if (m_instance == nullptr) {
			m_instance = new Singleton2();
		}
	}
	return m_instance;
}

#endif //__SINGLETON_H__

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值