您的位置:首页 > 其它

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

2017-08-04 17:08 225 查看
单例模式也叫单件模式。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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息