C++11的多种单例模型实现

单例概念

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例的懒汉实现

利用C++11的static线程安全性

class Instance {
public:
	static Instance& getInstance() {
		static Instance instance;
		return instance;
	}

private:
	Instance() = default;
};

利用call_once函数保证的只执行一次的语义;

class Instance {
public:
	static Instance& getInstance() {
		static std::once_flag flag;
		std::call_once(flag, []() {instance.reset(new Instance()); });
		return *instance;
	}

private:
	Instance() = default;

	static std::shared_ptr<Instance> instance;
};
std::shared_ptr<Instance> Instance::instance;

单例的饿汉实现

class Instance {
public:
	static Instance& getInstance() {
		return instance;
	}

private:
	Instance() = default;
	
	static Instance instance;
};
Instance Instance::instance;

或者

class Instance {
public:
	static Instance& getInstance() {
		return *instance;
	}

private:
	Instance() = default;

	static Instance* instance;
};
Instance* Instance::instance=new Instance();

有问题的双重检查锁

问题源码

//双重检测锁,存在问题
class Instance {
public:
	static Instance& getInstance() {
		if (instance == nullptr) {
			std::unique_lock<std::mutex>lock(mx);
			if (instance == nullptr)
				instance = new Instance();
		}
		return *instance;
	}

private:
	Instance() = default;
	static std::mutex mx;
	static Instance* instance;
};
Instance* Instance::instance;

以上代码的问题原因

instance = new Instance()执行有三个过程:
A. 分配内存;
B. 初始化构建对象;
C. 返回内存指针给instance变量;
其中根据依赖关系,A必然在B和C之前,其它的顺序是无法保证的;
看一下这种情况:
线程1:获取lock锁后,执行A->C,然后释放锁;
线程2:首先看到instance不为nullptr,直接返回instance,而这时候,instance还没有还得及被线程1初始化;
注:因为线程2没有加锁,不会有内存屏障。那么很自然地,加上内存屏障就可以避免这种情况了。

基于atomic的双重检查锁

源码

//双重检测锁
class Instance {
public:
	//或者这里的load和store采用默认的语义memory_order_seq_cst
	static Instance& getInstance() {
		if (instance.load(std::memory_order_acquire) == nullptr) {
			std::unique_lock<std::mutex>lock(mx);
			if (instance.load(std::memory_order_relaxed) == nullptr)
				instance.store(new Instance(), std::memory_order_release);
		}
		return *instance;
	}

private:
	Instance() = default;
	static std::mutex mx;
	static std::atomic<Instance*> instance;
};
std::atomic<Instance*> Instance::instance;

说明

C++11提供的atomic,可以实现内存屏障的作用。保证了线程1必须在store之前,将instance构建好(构建的3个过程是写操作,不会重排到store之后),这是由store的release的语义保证的。而线程2使用load的acquire语义保证了得到的instance必然是已经构建好的。
注:可以使用load/store的默认语义memory_order_seq_cst;
问:如果instance.load(std::memory_order_acquire)采用memory_order_relaxed语义,会有什么问题?

参考

https://www.cnblogs.com/muzzik/p/12298487.html
https://blog.csdn.net/iteye_10289/article/details/82551552

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值