何谓单例模式?
在一个项目中,全局范围内,某个类的实例有且仅有一个,通过这个唯一实例向其他模块提供数据的全局访问,这种模式就是单例模式。一个典范应用就是任务队列。
创建一个实例我们有如下方法:
- 构造函数
- 拷贝构造函数
- 赋值构造函数
- 移动构造函数
- 移动赋值函数
我们只必要一个实例, 那么就要限制获取实例的途径:
- 将构造函数私有化,在类内部只调用一次。
- 其他函数声明用 = delete 修饰
注意:
- 在类外部不能调用构造函数,所以在类内部创建的这个唯一的对象必须是静态的,如许就可以通过类名来访问了,为了不破坏类的封装,把这个静态对象的访问权限设置为私有的
- 在类中,静态成员函数才能访问其静态成员变量,所以给这个单例类提供一个静态函数用于得到这个静态的单例对象
单例模式的类的示例代码
因此,界说一个单例模式的类的示例代码如下:
- // 定义一个单例模式的类
- class Singleton {
- public:
- Singleton(const Singleton& oth) = delete;
- Singleton& operator=(const Singleton& oth) = delete;
- Singleton(Singleton&& oth) = delete;
- Singleton& operator=(Singleton&& oth) = delete;
- static Singleton* get_instance();
- private:
- Singleton() = default;
- static Singleton* instance_;
- }
复制代码 饿汉模式
所谓饿汉模式是在类加载的时间立即进行实例化,如许就得到了唯一的实例。
注意:类的静态成员变量在利用之前必须在类的外部进行初始化。
- class Singleton {
- public:
- Singleton(const Singleton& oth) = delete;
- Singleton& operator=(const Singleton& oth) = delete;
- Singleton(Singleton&& oth) = delete;
- Singleton& operator=(Singleton&& oth) = delete;
- static Singleton* get_instance() {
- return instance_;
- }
- private:
- Singleton() = default;
- static Singleton* instance_;
- }
- // 初始化
- Singleton* Singleton::instance_ = new Singleton;
- int main() {
- ......
- Singleton* ins = Singleton::get_instance();
- ......
- }
复制代码 懒汉模式
懒汉模式一如其名“懒得不行”,在类加载的时间并不进行对象的实例化,而是比及必要的时间再进行实例化。这可能会引发线程安全问题!
下面这段代码在单线程下没有问题,但在多线程环境下将出现线程安全问题。
- class Singleton {
- public:
- Singleton(const Singleton& oth) = delete;
- Singleton& operator=(const Singleton& oth) = delete;
- Singleton(Singleton&& oth) = delete;
- Singleton& operator=(Singleton&& oth) = delete;
- static Singleton* get_instance() {
- if (instance_ == nulllptr) {
- instance_ = new Singleton;
- }
- return instance_;
- }
- private:
- Singleton() = default;
- static Singleton* instance_;
- }
- // 类加载时并不进行实例化
- Singleton* Singleton::instance_ = nullptr;
- int main() {
- ......
- // 此时进行实例化
- Singleton* ins = Singleton::get_instance();
- ......
- }
复制代码 试想在多线程环境下,同时有两个线程调用了 get_instance(),它们看到的instance_ 都是nullptr,那么就会创建出两个实例!怎么解决呢?
互斥锁保护
- class Singleton {
- public:
- Singleton(const Singleton& oth) = delete;
- Singleton& operator=(const Singleton& oth) = delete;
- Singleton(Singleton&& oth) = delete;
- Singleton& operator=(Singleton&& oth) = delete;
- static Singleton* get_instance() {
- m_instance_.lock();
- if (instance_ == nulllptr) {
- instance_ = new Singleton;
- }
- m_instance_.unlock();
- return instance_;
- }
- private:
- Singleton() = default;
- static Singleton* instance_;
- static mutex m_instance_;
- }
- // 类加载时并不进行实例化
- Singleton* Singleton::instance_ = nullptr;
- mutex Singleton::m_instance_;
复制代码 这种简单利用互斥锁虽然可以解决线程安全问题,但是获取实例都必要上锁查抄(不仅第一次创建实例时必要上锁,纵然已经创建了实例仍然必要上锁查抄),效率实在低下!
双重查抄锁定
通过两个嵌套的if来判定单例对象是否为空的操作叫做双重查抄锁定
我们改进的思绪就是在加锁、解锁的代码块外层再添加一个判定,如许当任务队列的实例被创建出来之后,访问这个对象的线程就不会再实行加锁和解锁操作了。
- class Singleton {
- public:
- Singleton(const Singleton& oth) = delete;
- Singleton& operator=(const Singleton& oth) = delete;
- Singleton(Singleton&& oth) = delete;
- Singleton& operator=(Singleton&& oth) = delete;
- static Singleton* get_instance() {
- if (instance_ == nulllptr) {
- m_instance_.lock();
- if (instance_ == nullptr) {
- instance_ = new Singleton;
- }
- m_instance_.unlock();
- }
- return instance_;
- }
- private:
- Singleton() = default;
- static Singleton* instance_;
- static mutex m_instance_;
- }
- // 类加载时并不进行实例化
- Singleton* Singleton::instance_ = nullptr;
- mutex Singleton::m_instance_;
复制代码 万万没想到——可能会利用未创建好的实例?
到此仍然没有大功告成,因为万万没想到呆板指令会造成贫苦!
假设线程A实行到instance_ = new Singleton,线程B实行到第一个if (instance_ == nulllptr)。
instance_ = new Singleton在实行过程中对应的呆板指令可能会被重新排序。
正常过程如下:
但是被重新排序以后实行顺序可能会酿成如许:
线程B在进行指针判定的时间instance_ 指针是不为空的,但这个指针指向的内存却没有被初始化!
怎么解决呢?利用atomic 原子操作(不可中断的操作)。
- class Singleton {
- public:
- Singleton(const Singleton& oth) = delete;
- Singleton& operator=(const Singleton& oth) = delete;
- Singleton(Singleton&& oth) = delete;
- Singleton& operator=(Singleton&& oth) = delete;
- static Singleton* get_instance() {
- Singleton* obj = instance_.load();
- if (obj== nulllptr) {
- m_instance_.lock();
- obj = instance_.load();
- if (obj == nullptr) {
- obj = new Singleton();
- instance_.store(obj);
- }
- m_instance_.unlock();
- }
- return obj;
- }
- private:
- Singleton() = default;
- static mutex m_instance_;
- static atomic<Singleton*> instance_;
- }
- atomic<Singleton*> Singleton::instance_;
- mutex Singleton::m_instance_;
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |