镇江网站建设案例,杭州平面设计培训,wordpress加载图片慢,惊艳的网站一、概述 单例模式也称单态模式#xff0c;是一种创建型模式#xff0c;用于创建只能产生一个对象实例的类。例如#xff0c;项目中只存在一个声音管理系统、一个配置系统、一个文件管理系统、一个日志系统等#xff0c;甚至如果吧整个Windows操作系统看成一个项目#xf…一、概述 单例模式也称单态模式是一种创建型模式用于创建只能产生一个对象实例的类。例如项目中只存在一个声音管理系统、一个配置系统、一个文件管理系统、一个日志系统等甚至如果吧整个Windows操作系统看成一个项目那么其中只存在一个任务管理器窗口等。引入单例模式的实现意图保证一个类仅有一个实例存在同时提供能对该实例访问的全局方法。
二、单例模式分类
1、懒汉模式
1代码示例
class CSingletonImpl { public: static CSingletonImpl* GetInstance() { if (m_pInstance nullptr) { m_pInstance new CSingletonImpl; } return m_pInstance; } private: CSingletonImpl(){}; ~CSingletonImpl(){}; CSingletonImpl(const CSingletonImpl the); CSingletonImpl operator(const CSingletonImpl other); private: static CSingletonImpl* m_pInstance; };
CSingletonImpl*CSingletonImpl::m_pInstance nullptr;
2说明
单例模式为了防止多对象问题将构造函数析构函数拷贝构造函数赋值运算符函数设置为私有同时设置公有唯一接口方法来创建对象同时定义类静态指针。这是通用方法那么会有什么问题呢如果在单一线程中使用则没什么问题但是在多线程中使用则可能导致问题如果多个线程可能会因为操作系统时间片调度问题切换造成多对象产生那么解决这个问题的方案就是对GetInstance()成员函数枷锁。
示例代码
加入私有成员变量static std::mutex m_mutex;
static CSingletonImpl* GetInstance() { m_mutex.lock(); if (m_pInstance nullptr) { m_pInstance new CSingletonImpl; } m_mutex.unlock(); return m_pInstance; }
加入以上代码没有问题了吗呵呵还不行虽然对接口函数加锁从代码逻辑上没有问题实现了线程安全但是从执行效率上来说是有大问题的。当程序运行中GetInstance()可能会被多个线程频繁调用每次调用都会经历加锁解锁的过程这样的话会严重影响程序执行效率而且加锁机制仅仅对第一次创建对象有意义对象一旦创建则变成只读对象在多线程中对只读对象的访问加锁不仅代价大而且无意义。那么如何解决这个问题呢那就是双重锁定机制基于这种机制函数实现代码 static CSingletonImpl* GetInstance() { if (m_pInstance nullptr) { std::lock_guardstd::mutex siguard(si_mutex); if (m_pInstance nullptr) { m_pInstance new CSingletonImpl; } } return m_pInstance; }
上述双重锁定机制看起来比较完美但实际上存在潜在的问题内存访问重新排序导致双重锁定失效的问题比较推荐的方法时C11新标准的一些特性示例代码如下
#include mutex #include atomic
//通过原子变量解决双重锁定底层问题(load,store) class CSingletonImpl { public: static CSingletonImpl* GetInstance() { CSingletonImpl* task m_taskQ.load(std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); if (task nullptr) { std::lock_guardstd::m_mutex lock(m_mutex); task m_taskQ.load(std::memory_order_relaxed); if (task nullptr) { task new CSingletonImpl; std::atomic_thread_fence(std::memory_order_release); m_taskQ.store(task, std::memory_order_relaxed); } } return task; } private: CSingletonImpl(){}; ~CSingletonImpl(){}; CSingletonImpl(const CSingletonImpl the); CSingletonImpl operator(const CSingletonImpl other); private: static std::mutex m_mutex; static std::atomicCSingletonImpl* m_taskQ; };
std::mutex CSingletonImpl::m_mutex; std::atomicCSingletonImpl* CSingletonImpl::m_taskQ;
2、饿汉模式
1示例代码
class CSingletonImpl { public: static CSingletonImpl* GetInstance() { return m_pInstance; } private: CSingletonImpl(){}; ~CSingletonImpl(){}; CSingletonImpl(const CSingletonImpl the); CSingletonImpl operator(const CSingletonImpl other); private: static CSingletonImpl* m_pInstance; };
CSingletonImpl*CSingletonImpl::m_pInstance new CSingletonImpl();
2说明
此类模式可称为饿汉式--------程序一执行不管是否调用了GetInstance()成员函数这个单例类对象就已经被创建了。在饿汉式单例类代码的实现必须要注意如果一个项目中有多个.cpp源文件而且这些源文件中包含对全局变量的初始化代码例如某个.cpp中可能存在如下代码
int g_test CSingletonImpl::GetInstance()-m_i; //m_i是int类型变量
那么这样的代码是不安全的因为多个源文件中全局变量的初始化顺序是不确定的很可能造成GetInstance()函数返回是nullptr,此时去访问m_i成员变量肯定会导致程序执行异常。所以对饿汉式单例类对象的使用应该在程序入口函数开始执行后例如main函数后。
注意函数第一次执行时被初始化的静态变量与通过编译器常量进行初始化的基本类型静态变量这两种情况不要再单例类的析构函数中引用其他单例类对象。