单例模式
Posted qiu00
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。
单例模式:一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
要定义一个单例模式的类,首先将其构造函数私有化,以防止外界通过其创建对象。因为单例类仅有一个实例,所以创建一个类的私有静态指针变量,使其指向类的唯一实例。要使外界能够获取到私有静态指针变量指向的实例,所以要创建一个公有的静态函数。
1、懒汉版
1 class Singleton 2 { 3 private: 4 //构造函数私有化 5 Singleton() {}; 6 ~Singleton() {}; 7 Singleton(const Singleton&); 8 Singleton& operator=(const Singleton&); 9 10 private: 11 //指向唯一实例的私有静态指针变量 12 static Singleton *instance; 13 14 public: 15 //公有静态函数 16 static Singleton* getInstance() 17 { 18 if (instance == NULL) 19 { 20 instance = new Singleton(); 21 } 22 return instance; 23 } 24 }; 25 26 Singleton *Singleton::instance = NULL;
这个版本在单线程下是没有问题的,但是如果是多线程使用的话是不安全的。假设有两个线程调用getInstance(),instance=NULL,1线程执行到20行代码,此时2线程获的时间片,2线程同样会执行20行代码,这样就造成了线程的不安全。
2、加锁版
class Singleton { private: //构造函数私有化 Singleton() {}; ~Singleton() {}; Singleton(const Singleton&); Singleton& operator=(const Singleton&); private: //指向唯一实例的私有静态指针变量 static Singleton *instance; public: //公有静态函数 static Singleton* getInstance() { Lock lock; if (instance == NULL) { instance = new Singleton(); } return instance; } }; Singleton *Singleton::instance = NULL;
这样的是线程安全的,但是锁的使用会降低性能,如果是web高并发下,这种版本效率太低。
3、双检查锁版
1 class Singleton 2 { 3 private: 4 //构造函数私有化 5 Singleton() {}; 6 ~Singleton() {}; 7 Singleton(const Singleton&); 8 Singleton& operator=(const Singleton&); 9 10 private: 11 //指向唯一实例的私有静态指针变量 12 static Singleton *instance; 13 14 public: 15 //公有静态函数 16 static Singleton* getInstance() 17 { 18 if(instance == NULL) 19 { 20 //基于作用域加锁 21 Lock lock; 22 if (instance == NULL) 23 { 24 instance = new Singleton(); 25 } 26 }//超出作用域解锁 27 return instance; 28 } 29 }; 30 31 Singleton *Singleton::instance = NULL;
这个版本下,按前面所假设情况,2线程会阻塞在21行,直到1线程执行完毕,但是这个版本还是有问题。在24行,我们通常所认为的指令执行顺序应该是:先创建内存,然后构造器构造,最后将内存地址返回,但是很多编译器会对程序进行优化,优化后的指令执行顺序有可能会是:先创建内存,然后将内存地址返回,最后构造器构造。这样的话有可能2线程在18行判断instance!=NULL,直接返回instance,然后直接使用instance指向的内存中的数据,但是内存中可能没有数据,此时程序就出现了错误。
解决办法:
1 class Singleton 2 { 3 private: 4 //构造函数私有化 5 Singleton() {}; 6 ~Singleton() {}; 7 Singleton(const Singleton&); 8 Singleton& operator=(const Singleton&); 9 10 private: 11 //指向唯一实例的私有静态指针变量 12 static std::atomic<Singleton *> m_instance; 13 static std::mutex m_mutex; 14 15 public: 16 //公有静态函数 17 static Singleton* getInstance() 18 { 19 Singleton *tmp= m_instance.load(std::memory_order_relaxed); 20 //获取内存fence,tmp不被编译器优化 21 std::atomic_thread_fence(std::memory_order_acquire); 22 if(tmp==nullptr) 23 { 24 std::lock_guard<std::mutex> lock(m_mutex); 25 tmp= m_instance.load(std::memory_order_relaxed); 26 if(tmp==nullptr) 27 { 28 tmp= new Singleton; 29 //释放fence 30 std::atomic_thread_fence(std::memory_order_release); 31 m_instance.store(tmp, std::memory_order_relaxed); 32 } 33 } 34 return tmp; 35 } 36 };
以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章