单例模式是基本的设计模式之一,它属于创建型模式,提供一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
在程序设计上须保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
在进行单例类封装的时候,避免被调用方构造,构造函数需私有化。
程序启动时就实例化了该对象,并没有推迟到第一次使用该对象时再进行实例化。
在这样的情况下,多线程调用单例和它的方法,只会进行实例的使用而不会进行实例的创建。
为什么说它是线程安全的呢?
因为C++11保证静态局部变量的初始化过程是线程安全的,一个线程在初始化 m 的时候,其他线程执行到 m 的初始化这一行的时候,就会挂起。
代码实现如下:
class Singleton{
public:
static Singleton* m_instance_ptr;
static Singleton* get_instance() {
return m_instance_ptr;
}
private:
Singleton() { }
};
Singleton* Singleton::m_instance_ptr = new Singleton();
int main(){
Singleton* instance1 = Singleton::get_instance();
Singleton* instance2 = Singleton::get_instance();
return 0;
}
一句话总结:因为类实例化就会占用内存,所以浪费资源,但由于提前初始化了实例,所以效率较高,且不存在线程安全问题。
由于程序流程是程序员控制的,所以在饿汉模式下,只要能保证前期能创建实例,就不存在那些抢占多实例的问题。
如果在程序运行过程中没有使用到实例对象,那么在饿汉模式下该实例对象就被浪费掉了,这个场景可能需要使用懒汉单例模式。
懒汉单例模式会在第一次使用它的时候才进行对象的实例化。
代码实现如下:
class Singleton {
public:
static Singleton* m_instance_ptr;
static Singleton* get_instance() {
if(m_instance_ptr == NULL)
m_instance_ptr = new Singleton();
return m_instance_ptr;
}
private:
Singleton() { }
};
Singleton* Singleton::m_instance_ptr = NULL;
int main(){
Singleton* instance1 = Singleton::get_instance();
Singleton* instance2 = Singleton::get_instance();
return 0;
}
如果多线程竞争创建实例时,需要考虑加锁。
代码实现如下:
class Singleton {
public:
static Singleton* m_instance_ptr;
static std::mutex m_mutex;
static Singleton* get_instance() {
// double-check来保证不会每次进来都去获取锁,来提高性能,先判断不为空的话,直接返回了数据
if(m_instance_ptr == NULL) {
m_mutex.lock();
if(m_instance_ptr == NULL)
m_instance_ptr = new Singleton();
m_mutex.unlock();
}
return m_instance_ptr;
}
private:
Singleton() { }
};
Singleton* Singleton::m_instance_ptr = NULL;
std::mutex Singleton::m_mutex;
void thread_instance_do() {
Singleton* instance = Singleton::get_instance();
while(1) {
// do something
}
}
int main(){
std::thread thread1(thread_instance_do);
std::thread thread2(thread_instance_do);
thread1.join();
thread2.join();
return 0;
}
一句话总结:由于延迟加载,所以节省资源,导致效率低,且存在线程安全问题。
参考文章