单例模式4种实现方式C++

单例模式:保证一个类只有一个实例,并提供一个该实例的全局访问点

实现方式:构造和拷贝构造设为私有,总共介绍四个版本,推荐使用最后一个。版本1、2、3、4全都是懒汉式的写法。

版本1:线程非安全版本

在多线程情况下可能会同时创建出多个对象,比如,现在有线程A和线程B,线程A进入到16行时, 线程B进入17行,会容易new出多个实例。

版本2:线程安全,但锁的代价过高

在GetInstance()中使用局部变量锁, 保证同一时刻只有一个线程访问30-33行。
读变量没必要加锁,尤其是在高并发的情况下,代价还是挺高的。

版本3:双检查锁,但由于内存读写reorder(重新排序)不安全(导致双检查锁的失效)

锁前检查,避免都是读取操作时锁代价过高的问题
锁后检查,避免两个线程同时进入,从而new了两个实例

因为编译器优化,指令的执行顺序可能reorder(CPU执行指令的层次,而且线程是在指令层次抢时间片的) ,可能变成这样:分配内存->赋值->调用构造(理想应该是:分配内存->调用构造->赋值)
假设在reorder的情况下:线程1走到赋值,但还没调用构造的阶段,而线程2进来判断m_instance,此时它已经被复制 所以不为空,这时候线程2就直接返回m_instance,但事实上它还没构造出来……这就尴尬了,实际上因为它没有构造,肯定是不能用的。
总之,就是双检查锁 它欺骗了线程2……

怎么解决这个问题呢?
Java 和c sharp 添加了一个关键字:volatile
这样,编译时在编译的时候就知道,这个变量的整个赋值过程不能reorder,需要按照常规的流程走。

C++11之后 跨平台实现了volatile
还是挺复杂的哈……
在这里插入图片描述

前3个版本均来自侯捷老师的视频,可以去观看一下,讲的灰常简单易懂

版本4:局部静态变量实现单例模式,线程安全(推荐使用)
class Singleton{
public:
	~Singleton();
	static Singleton& getInstance()
	{
		static Singleton instance;
		return instance;	
	}
private:
	Singleton();
};

原因是C++ 11标准中新增了一个特性叫Magic Static:如果变量在初始化时,并发线程同时进入到static声明语句,并发线程会阻塞等待初始化结束。
这样可以保证在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性,同时也避免了new对象时指令重排序造成对象初始化不完全的现象。并且相比较与使用智能指针以及mutex来保证线程安全和内存安全来说,这样做能够提升效率。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值