0 懒汉与饿汉
对于Singleton单例模式我们并不陌生,但我们常用的多是饿汉模式:
Singleton实例的声明和实例化在instance()函数中同时完成。
而懒汉模式要求,Singleton实例的声明和实例化分开:
先声明Singleton实例对象,然后调用时才在instance()中完成实例化。
具体见下面代码。
1 饿汉模式
没有事先声明Log单例对象,Log实例同时完成声明和实例化。
class Log
{
public:static Log *get_instance(){static Log instance;// Log实例同时完成声明和实例化return &instance;}private:// 构造、析构等函数
}
2 懒汉模式
先声明单例对象,等到需要时再通过instance()函数完成实例化。
class Log {
public:static Log *get_instance() {if (instance == nullptr) {// 判空instance = new Log();// 完成实例化}return instance;}private:static Log *instance;// 先声明Log单例对象// 构造、析构等函数
}
3 为保证线程安全而引出的DCL双重检查
上述两种单例模式都不能保证线程安全,因此需要进行一些加锁。
3.1 线程安全的Singleton饿汉模式
下面这个例子就是线程安全的Singleton饿汉模式(出自muduo源码)。
template<typename T>
class Singleton : noncopyable
{public:// 私有成员函数Singleton() = delete;// delete声明该函数为私有函数 =default表示保留~Singleton() = delete;// 该类唯一的公有成员static T& instance(){// 子进程?pthread_once(&ponce_, &Singleton::init);// pthread_once()保证init()函数只被调用一次 还能保证线程安全assert(value_ != NULL);return *value_;}private:static void init(){// 创建类T的唯一实例value_ = new T();// 程序结束时自动销毁该对象if (!detail::has_no_destroy<T>::value){// atexit()注册销毁函数destroy()::atexit(destroy);}}
3.2 线程安全的Singleton懒汉模式与DCL双重检查
下面的例子是线程安全的Singleton懒汉模式。
在这种模式中,需要对INSTANCE单例对象进行两次判空(即Double Check Lock :DCL双重检查)以保证效率和线程安全。
- 第一层判空是为了提高效率,即当有一个线程new出来对象后第二个线程就不用竞争第一个线程的对象锁而进行等待;
- 第二层判空是为了保证线程安全,多线程运行过程中很有可能同时运行到if(INSTANCE==null),虽然会在外面等待由另一个线程执行完再执行,也会存在创建多个实例的问题;
具体可参考:https://www.cnblogs.com/zhangzhonghui/p/11460731.html
和https://blog.csdn.net/weixin_43730516/article/details/113091728
4 参考材料
- https://www.cnblogs.com/zhangzhonghui/p/11460731.html
- https://blog.csdn.net/weixin_43730516/article/details/113091728