C++实现线程安全单例类
来源:互联网 发布:彩票计划软件 编辑:程序博客网 时间:2024/06/05 23:55
1.什么是单例模式
单例模式是一种非常常用的设计模式,几乎在稍大的工程项目中都会用到。单例类保证在全局中只有唯一一个类的实例对象,并且在类的内部提供了获取这个唯一实例的接口。在类中,实例化出一个对象需要调用构造函数,为了防止在类的外部调用构造函数构造出实例,对类的构造函数就应有所限制,可以将构造函数的访问权限设置为private或protected。还要提供一个访问实例的接口,在类中定义一个static函数,返回类内部唯一的实例对象即可。
2.构造一个实例类
class Singleton{private: Singleton() //构造函数声明为私有的 :_a(0) {} //防拷贝 Singleton(const Singleton&); Singleton& operator=(const Singleton&);public: static Singleton* GetInstance() //声明一个静态类型的接口来获取实例 { if (_inst == NULL) { _inst = new Singleton(); } return _instance; }private: int _a; //指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例 static Singleton* _instance;};Singleton* Singleton::_instance= NULL; //初始化静态成员变量
构造函数能创建一个实例对象,但是拷贝构造函数和赋值拷贝构造函数同样可以,所以,可以用防拷贝的方法只声明不定义,避免了多次创建实例对象。
上面的代码,看起来貌似没有什么问题,但是若是在多线程下,有可能会创建多个Singleton实例,所以为了防止这种情况,我们必须保证创建对象的过程时原子操作,则可以考虑加锁。C++11的mutix头文件中已经提供了lock和unlock,我们可以直接使用。
#include <mutex>class Singleton{private: Singleton() //构造函数声明为私有的 :_a(0) {} //防拷贝 Singleton(const Singleton&); Singleton& operator=(const Singleton&);public: static Singleton* GetInstance() //声明一个静态类型的接口来获取实例 { if (_instance== NULL) { lock_guard<mutex> lock(_mtx); if (_instance== NULL) { _inst = new Singleton(); } } return _instance; } void DelInstance() //销毁实例 { lock_guard<mutex> lock(_mtx); if (_instance) { delete _instance; _inst = NULL; } }private: int _a; static Singleton* _instance;//声明一个静态成员作为类的实例 static mutex _mtx;//保证线程安全的互斥锁};Singleton* Singleton::_instance = NULL; //初始化静态成员变量mutex Singleton::_mtx;
以上代码中加了互斥锁,保证了线程安全。但也不是绝对安全的,因为加锁或解锁会引入新的问题,有可能抛异常或者造成死锁。解决了线程安全,那么就可以进一步提高效率。在上述代码中,可以看到用了两次判断,也叫双检查机制,这就保证了只在第一次获取对象时加锁,避免高并发场景下每次获取实例对象都进行加锁,提高了效率。
3.具体模式
懒汉模式:在第一次调用GetInstance的时候才实例化出对象,此后返回的都是该对象。相对饿汉模式而言,复杂,要确保线程安全问题,但在各种场景下都适用。
饿汉模式:无论是否需要该类的实例,在程序一开始的时候会产生该类的实例对象,此后返回的都是该对象。由于是在main函数之前创建线程,可能会出现不确定问题,适用性受到限制。
上面代码实现的是懒汉模式,那么下边就实现饿汉模式
namespace Hungry{ class Singleton { private: Singleton() //构造函数声明为私有的 :_a(0) {} //防拷贝 Singleton(const Singleton&); Singleton& operator=(const Singleton&); public: //static Singleton& GetInstance() //{ // assert(_instance); // return *_instance; //} static Singleton& GetInstance() //在类内部创建静态实例,全局只此一份 { static Singleton _instance; return _instance; } void DelInstance() //销毁实例 { if (_instance) { delete _instance; _instance = NULL; } } private: int _a; static Singleton* _instance; }; Singleton* Singleton::_instance = NULL; //Singleton* Singleton::_instance = new Singleton();//全局生成一个实例对象 }
定义为全局或者静态的变量和对象都是在调用main()函数之前就已经初始化好的,此时只有一个线程,所以不会存在线程安全的问题。
4.双检查隐患
指令流水作业时,系统可能会为了提高效率,将执行流的顺序打乱,这时候进行外部的检查时,可能内部的代码还没有完全执行完,但是已经满足了外部的判断条件,那么执行就会出错。
比如,创建一个实例对象,正确的执行流是:
1.operator new创建空间
2.调用构造函数初始化成员变量
3.赋值
打乱之后的:
1.operator new创建空间
2.赋值
3.调用构造函数初始化成员变量
这时候为了避免这种情况,可以使用内存屏障MemoryBarrier,保证其之前和之后的顺序流不被打乱。
static Singleton* GetInstance(){ if (_instance == NULL) { lock_guard<mutex> lock(_mtx); if (_instance == NULL) { Singleton* tmp = new Singleton; MemoryBarrier(); //使用内存栅栏 _instance = tmp; } } return _instance;}
参考:http://www.jellythink.com/archives/82
- C++实现线程安全单例类
- 线程安全C/C++
- 线程安全C/C++
- linux多线程编程(C):信号量实现的线程安全队列
- linux多线程编程(C):互斥量实现的线程安全队列
- C++简洁实现线程安全单例类
- Vector实现线程安全
- 线程安全实现方案
- 如何实现线程安全?
- JAVA实现线程安全
- 实现Java线程安全
- 【C/C++】什么是线程安全
- C runtime lib 线程安全
- C/C++ 线程安全队列
- ConcurrentHashMap线程安全的实现
- python实现线程安全队列
- Mesos中实现线程安全
- Map的线程安全实现
- Git版本切换
- android studio加载RecyclerView,找不到RecyclerView的问题
- 【Selenium】7截屏
- JAVA语言递归方法创建二叉树
- 数据库SQL实战
- C++实现线程安全单例类
- opencv基本函数使用
- js对含有日期的json格式的字符对象进行排序
- 编译成功
- python 统计每月用户注册量(总注册数)
- 在tomcat里面配置数据库地址,以及在Spring和Java中的使用
- UVA
- 基于Spring Cloud的微服务构建学习-1 基础知识
- 12c-Oracle 12c R2 注意事项:login.sql 改变