单例模式(饿汉模式和懒汉模式)

单例模式也叫单件模式。Singleton是一个非常常用的设计模式,几乎所有稍微大一些的程序都会使用到它,所以构建一个线程安全并且高效的Singleton很重要。
单例模式的特点:
1>单例类保证全局只有一个唯一实例对象。
2>单例类提供获取这个唯一实例的接口。

1.下面先简单设计一个单例模式
这里写图片描述
这里写图片描述
但是这样设计会有线程安全的问题。为什么说这样实现的一个单例模式是线程不安全的呢?是因为暴露给外部接口的函数中_inst=new Singleton;是非原子操作,易出现线程安全问题。
2. 针对线程安全有以下解决方法:
(一)加锁
(二)使非原子操作变为原子的,一般系统会提供一个原子性操作函数(给CPU做一个标记,告诉CPU只要该操作开始执行就不能被切换出去)
下面主要以第一种方式来解决问题!
在Linux下POSIX机制中有lock,unlock等,但在Windows下一般不提供锁机制,使用临界区的形式,但是在C++11库中提供了锁机制。
先引入头文件:#include
定义锁变量:
这里写图片描述
在类外进行初始化:
这里写图片描述
给线程不安全的地方加锁:
这里写图片描述
但是加锁后引来一个性能问题,单例模式的设计我们讲究高效,所以在这基础上还应该优化,可以采取双重检查的方式。
这里写图片描述
上面加锁后又会引发死锁情况,如果在_inst=new Singleton;这里分配空间失败,可能会抛出异常,这时去捕获异常的话就会引发死锁的情况,针对这种情况我们可以用RAII机制去解决,类似于智能指针。
这里写图片描述
这里写图片描述
写好的代码经过编译后为可执行的二进制文件给CPU去执行,CPU可能会优化一些指令,比如:_inst=new Singleton;分为三步去执行:一是分配空间,二是调构造函数,三是赋值。加入双重检查可能会带来的一个问题是CPU对指令优化从而打乱执行顺序导致错误,如果二三步骤颠倒,没有调构造函数前可能为一个随机数或野指针,如果这时赋值,然后这时该线程被切出去,再来一个线程检查它不为0或NULL就直接返回该值导致一些意想不到的问题。我们可以调用windows系统下的函数MemoryBarrier(); //内存栅栏函数来保证下面的tmp是一个完整的对象指针。
这里写图片描述
该种就是最终版的懒汉单例模式(线程安全的且高效).

3.下面再介绍两种饿汉单例模式:
(1)利用静态成员变量初始化在调main函数之前的特性实现,这种方式没有加锁,没有双重检查,也不用考虑异常捕获造成的内存泄漏。
这里写图片描述
这里写图片描述
(2)在调用接口函数获得对象时才临时创建
这里写图片描述

4.饿汉模式用于静态联编没有问题,但是在动态联编会出现问题。
静态编译是将链接库都加载到该目标代码中,但是动态编译是运行起来才将链接文件加载进来,这会儿用饿汉模式可能对象的初始化会存在问题。

5.完整代码已上传至github
Singleton.cpp

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值