单例模式 【饿汉式、懒汉式、线程安全、单例资源释放】
单例模式,保证整个工程中,有且仅有一个该类的实例对象。
一、饿汉式单例
二、懒汉式单例
三、创建单例的线程安全
多线程情景中创建单例, 单例类的静态单例对象数据成员此时作为共享数据,那么势必有必要保证 获取单例的函数是线程安全的。 这里通过使用C++11新标准版本的线程库函数,来完成对线程安全的单例获取函数的编写。分两步改造,一个改版,以及一句总结 分享给大家。
///< Single.h----------------------------- //仅仅针对 static Single * instance() 函数的编写, 其他的忽略, 请阅读其他小节内容。 //线程安全, 保护共享数据,毫无疑问,应采用 “加锁” , 这里使用 互斥量。 ====== 第一版 ====== public: static Single * instance() { std::unique_lock<std::mutex> guardLock ( m_mutex); ///< 【核心点1】确保同一时刻,仅有一个线程能够执行 if (m_instance == nullptr) { m_instance = new Single(); static AutoClearSingle autoClearSingle; } return m_instance; } private: std::mutex m_mutex; 第一版问题点: 每次获取单例对象, 都需要等待锁, 而实际上,m_instance 为 nullptr,仅第一次获取单例对象时成立。 假如线程很多, 获取单例对象的调用次数频繁,这将极大的降低代码的执行效率。 ====== 第二版 ====== public: static Single * instance() { if (m_instance == nullptr) { ///<【核心点1】 双重检查 (又叫:双重锁定) std::unique_lock<std::mutex> guardLock ( m_mutex); ///<【核心点2】 if (m_instance == nullptr) { m_instance = new Single(); static AutoClearSingle autoClearSingle; } } return m_instance; } private: std::mutex m_mutex; 解析: 多出的第一个判断 if (m_instance == nullptr) 如果 m_instance != nullptr , 条件成立, 则肯定 m_instance 被 new 过了 。 如果 m_instance == nullptr , 不一定代表, m_instance一定没被 new 过。 因此需要上锁。 简简单单的多一行双重检查机制, 能够大大的提高效率, 同时有能保证线程安全。 ====== 改版 ====== public: static Single * instance() { std::call_once (m_call_once_flag, createInstance); ///< 【核心点1】 return m_instance; } private: static void createInstance() { m_instance = new Single(); static AutoClearSingle autoClearSingle; } std::once_flag m_call_once_flag; ///< 【核心点2】 标记 std::call_once 传入的可调用对象,是否被调用过。 备注:createInstance函数可以使用 lambda 代替, 让代码更简洁。 解析: std::call_once 是 C++11 引入的新函数。 能够保证传入的可调用对象,只被调用一次。std::call_once 具备互斥量的能力, 而且传言在效率上比互斥量消耗更少的资源。 ====== 总结 ====== 大道至简: 在子线程中访问获取单例对象函数,需要保证线程安全。 那么,真实开发时,如非迫不得已, 在主线程中,创建子线程之前, 先调用一次获取单例对象的函数, 所有的麻烦事都会烟消云散。
四、单例对象的资源释放
编写单例类时,往往会忽视对单例对象资源的释放。这里采用 “静态对象只会创建一次,在程序退出时自动销毁 ” 以及 “ 内部类 ” 两个思想,分享一种精致的设计方案,。
///< Single.h------------------------------------ class Single { private: Single() {} ///<私有化构造函数,杜绝内外创建类对象。 static Single * m_instance; ///<静态成员 public: static Single * instance(); class AutoClearSingle ///<内部类, 用来释放单例对象 【核心点1】 { public: ~AutoClearSingle() { if ( Single::m_instance) { delete Single::m_instance; Single::m_instance = nullptr; } } }; ///< class AutoClearSingle }; ///< class Single ///< Single.cpp---------------------------------- Single * Single::m_instance = nullptr; Single * Single::instance() { if (m_instance == nullptr) { m_instance = new Single(); static AutoClearSingle autoClearSingle; ///< 【核心点2】 } return m_instance; }
。。。。看看曾经写的一个单例随笔, 现在回头看,想法真是奇怪的很。。哈哈哈。。。 单例模式 代码例子