定义
只提供唯一一个类的实例,具有全局变量的特点,在任何位置都可以通过接口获取到那个唯一实例
具体运用场景
1.设备管理器,系统中可能有多个设备,但是只有一个设备管理器,用于管理设备驱动;
2.数据池,用来缓存数据的数据结构,需要在一处写,多处读取或者多处写,多处读取;
要点
1.全局只有一个实例:static 特性,同时禁止用户自己声明并定义实例(把构造函数设为 private)
2.禁止赋值和拷贝
3.用户通过接口获取实例:使用 static 类成员函数
4.线程安全
实现
示例一、没有考虑线程安全的方式
class Singleton{
private:
Singleton(){
//......
}
Singleton(Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* instance_ptr;
public:
~Singleton(){
//.....
}
static Singleton* get_instance(){
if(instance_ptr==nullptr){
instance_ptr = new Singleton;
}
return instance_ptr;
}
};
Singleton* Singleton::instance_ptr = nullptr;
int main(){
Singleton* instance = Singleton::get_instance();
return 0;
}
线程安全:上述中,可能会造成多个线程对共享内存数据访问的竞争条件的形成:即,第一个线程在访问if语句时判断instance_ptr
为空,开始实例化单例。同时,第二个线程访问单例,判断到if还是为空,也开始实例化单例。
C++标准中对数据竞争的定义是:多个线程并发的去修改一个独立对象,数据竞争是未定义行为的起因。
内存泄漏:这个类没有负责delete对象,需要使用者自己delete,因此存在内存泄漏的风险。
示例二、线程安全的方式
//include<mutex>
class Singleton{
private:
Singleton(){
//......
}
Singleton(Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* instance_ptr;
static std::mutex m_mutex;
public:
~Singleton(){
//.....
}
static Singleton* get_instance(){
if(instance_ptr==nullptr){
std::lock_guard<std::mutex> lk(m_mutex);
if(instance_ptr==nullptr) {
instance_ptr = new Singleton;
}
}
return instance_ptr;
}
};
// init static member
Singleton* Singleton::instance_ptr = nullptr;
int main(){
Singleton* instance = Singleton::get_instance();
return 0;
}
加锁,这里使用了两个 if判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁,避免每次调用 get_instance都加锁。
互斥:是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
看起来很美好,但这样是有问题的,在某些平台,双重检查锁定模式会失效!具体可以看这篇文章
示例三、c++11中的静态局部变量的线程安全
class Singleton{
private:
Singleton(){
//......
}
Singleton(Singleton&);
Singleton& operator=(const Singleton&);
public:
~Singleton(){
//.....
}
static Singleton& get_instance(){
static Singleton instance;
return instance;
}
};
这种方法叫做 Meyers‘ Singleton ,是Meyers提出的。所用到的特性是在C++11标准中的Magic Static特性:
如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。
原文:https://www.cnblogs.com/Redwarx008/p/12235125.html