(一)简单介绍
单例模式分为两种类型:懒汉模式和饿汉模式。
懒汉模式:在实际类对象被调用时才会产生一个新的类实例,并在之后返回这个实例。多线程环境下,多线程可能会同时调用接口函数创建新的实例,为了防止这种并发情况,需要采用锁机制来保证线程安全性,但由于多线程抢占互斥锁,可能会导致系统的性能瓶颈。
饿汉模式:在程序开始时就会产生一个该类的实例,并在之后返回这个实例。由于在进入主函数之前就由主线程完成了对象的初始化化,所以不用担心多线程问题,也不存在线程安全问题。
(二)懒汉模式举例
#include <iostream> #include <pthread.h> #include <stdio.h> #include <stdlib.h> using namespace std; class Singleton { private: Singleton() { cout<<"class create"<<endl; } static Singleton *m_instance; static pthread_mutex_t mutex; class Garbo { public: ~Garbo() { if (Singleton::m_instance) { cout<<"class end"<<endl; delete Singleton::m_instance; } } }; static Garbo garbo; public: static Singleton *getInstance(); // ~Singleton() // { // cout<<"class end"<<endl; // delete m_instance; // } }; Singleton* Singleton::m_instance = nullptr; pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER; Singleton::Garbo Singleton::garbo; Singleton* Singleton::getInstance() { if (m_instance == nullptr) { pthread_mutex_lock(&mutex); if (m_instance == nullptr) m_instance = new Singleton(); pthread_mutex_unlock(&mutex); }
return m_instance; } int main(void) { Singleton *singleton = Singleton::getInstance(); return 0; }
上述代码中,在类实例初始化时才调用getInstance创建一个新实例,为了保证线程安全(即互斥),定义互斥锁。将类构造函数定义为private,防止类外部直接调用构造函数。
两次加锁:
假设现在有多个线程欲创建新对象,当前m_instance为nulltpr,进入第一层if,所有线程抢占互斥锁,其中一个线程完成对类实例的创建后解锁并返回,此时唤醒其他线程,当前m_instance已被创建,其他线程将不予以创建新实例,直接返回。从这里也可以看出,由于线程之间对锁的抢占,会有一定的性能开销。
关于单例模式的析构函数:
由于singleton对象是一个static对象,直接delete singleton是不能够调用类析构函数的(注释内容),要想释放m_instance实例,需要内嵌一个class,并初始该静态类成员,当类成员释放时调用其析构函数从而释放m_instance实例。
另一种写法:
测试代码:
class Singleton { private: Singleton(){} public: static Singleton* getInstance() { Lock(); //not needed after C++0X static Singleton instacne; Unlock; //not needed after C++0X return instance; } }
上述在C++0X以后,要求编译器保证内部静态变量的线程安全性,可以不加锁。但在C++0X以前,仍然需要加锁。
(三)饿汉模式举例
在程序开始时就会产生一个类实例,并在以后返回该实例。
测试代码:
class Singleton { private: Singleton(){} static Singleton *m_instance; public: static Singleton *getInstance(void) { return m_instance; } }; Singleton* Singleton::m_instance = new Singleton();