设计模式十:单件模式 (Singleton Pattern)
单件模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。
单例模式有两种主要的初始化方式:饱汉模式(Lazy Initialization)和饿汉模式(Eager Initialization)。它们在实例创建的时机上有显著区别。
1. 饱汉模式 (Lazy Initialization)
饱汉模式也称为\"懒加载\"模式,只有在第一次请求实例时才创建单例对象。
特点:
-
延迟初始化:实例在第一次调用
getInstance()
时才被创建 -
节省资源:如果从未使用单例,则不会创建实例
-
需要处理线程安全问题
基础实现(非线程安全):
class Singleton {private: Singleton() {} static Singleton* instance; public: static Singleton* getInstance() { if (instance == nullptr) { instance = new Singleton(); } return instance; } // 删除拷贝构造函数和赋值运算符 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;};Singleton* Singleton::instance = nullptr;
线程安全版本:
#include class Singleton {private: Singleton() {} static Singleton* instance; static std::mutex mtx; public: static Singleton* getInstance() { std::lock_guard lock(mtx); if (instance == nullptr) { instance = new Singleton(); } return instance; } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;};Singleton* Singleton::instance = nullptr;std::mutex Singleton::mtx;
此方法线程安全,但是锁的代价太高,每次访问都需要加锁,开销较大,需要优化 。
双检查锁(但由于内存读写reorder不安全) :
#include class Singleton {private: Singleton() {} static Singleton* instance; static std::mutex mtx; public: static Singleton* getInstance() { if (instance == nullptr) { // 第一次检查 std::lock_guard lock(mtx); if (instance == nullptr) { // 第二次检查 instance = new Singleton(); } } return instance; } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;};Singleton* Singleton::instance = nullptr;std::mutex Singleton::mtx;
正常的逻辑是(1、先分配内存,2、最后再构造 ,3、把内存分配给instance ),但是由于内存读写reorder,可能会导致其他线程访问在第一次检查时读取了reorder(可能最开始的线程1、先分配内存,2、把内存分配给instance ,3、最后再构造)的非正确值(第2步的内存),出现错误。
C++11版本之后的跨平台实现(volatile) (线程安全)
std::atomic Singleton::m_instance;std::mutex Singleton::m_mutex;Singleton* Singleton::getInstance() { // 1. 首先以宽松内存序读取当前实例 Singleton* tmp = m_instance.load(std::memory_order_relaxed); // 2. 获取内存栅栏,确保后续读取操作能看到之前的所有写入 std::atomic_thread_fence(std::memory_order_acquire); if (tmp == nullptr) { std::lock_guard lock(m_mutex); tmp = m_instance.load(std::memory_order_relaxed); if (tmp == nullptr) { tmp = new Singleton; // 3. 释放内存栅栏,确保新对象的构造对所有处理器可见 std::atomic_thread_fence(std::memory_order_release); // 4. 以宽松内存序存储新实例 m_instance.store(tmp, std::memory_order_relaxed); } } return tmp;}
2. 饿汉模式 (Eager Initialization)
饿汉模式在程序启动时(静态初始化阶段)就创建单例实例。
特点:
-
提前初始化:实例在程序启动时就被创建
-
线程安全:因为实例在main()函数执行前就已创建
-
可能浪费资源:即使从未使用单例,实例也会被创建
实现:
class Singleton {private: Singleton() {} static Singleton* instance; public: static Singleton* getInstance() { return instance; } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;};// 在程序启动时就初始化实例Singleton* Singleton::instance = new Singleton();
使用静态变量的饿汉模式:
class Singleton {private: Singleton() {} public: static Singleton& getInstance() { static Singleton instance; // 静态变量在首次使用时初始化(C++11保证线程安全) return instance; } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;};