IT评测·应用市场-qidao123.com
标题:
设计模式之单例模式
[打印本页]
作者:
曹旭辉
时间:
2024-6-22 06:35
标题:
设计模式之单例模式
何谓单例模式?
在一个项目中,全局范围内,某个类的实例有且仅有一个,通过这个唯一实例向其他模块提供数据的全局访问,这种模式就是单例模式。一个典范应用就是任务队列。
创建一个实例我们有如下方法:
构造函数
拷贝构造函数
赋值构造函数
移动构造函数
移动赋值函数
我们只必要一个实例, 那么就要
限制获取实例的途径
:
将构造函数私有化,在类内部只调用一次。
其他函数声明用 = delete 修饰
注意:
在类外部不能调用构造函数,所以在
类内部创建的这个唯一的对象必须是静态
的,如许就可以通过类名来访问了,为了不破坏类的封装,把这个静态对象的访问权限设置为私有的
在类中,
静态成员函数才能访问其静态成员变量
,所以给这个单例类提供一个静态函数用于得到这个静态的单例对象
单例模式的类的示例代码
因此,界说一个单例模式的类的示例代码如下:
// 定义一个单例模式的类
class Singleton {
public:
Singleton(const Singleton& oth) = delete;
Singleton& operator=(const Singleton& oth) = delete;
Singleton(Singleton&& oth) = delete;
Singleton& operator=(Singleton&& oth) = delete;
static Singleton* get_instance();
private:
Singleton() = default;
static Singleton* instance_;
}
复制代码
饿汉模式
所谓饿汉模式是在
类加载的时间立即进行实例化
,如许就得到了唯一的实例。
注意:
类的静态成员变量
在利用之前
必须在类的外部进行初始化
。
class Singleton {
public:
Singleton(const Singleton& oth) = delete;
Singleton& operator=(const Singleton& oth) = delete;
Singleton(Singleton&& oth) = delete;
Singleton& operator=(Singleton&& oth) = delete;
static Singleton* get_instance() {
return instance_;
}
private:
Singleton() = default;
static Singleton* instance_;
}
// 初始化
Singleton* Singleton::instance_ = new Singleton;
int main() {
......
Singleton* ins = Singleton::get_instance();
......
}
复制代码
懒汉模式
懒汉模式一如其名“懒得不行”,在类加载的时间并不进行对象的实例化,而是比及必要的时间再进行实例化。
这可能会引发线程安全问题!
下面这段代码在单线程下没有问题,但在多线程环境下将出现线程安全问题。
class Singleton {
public:
Singleton(const Singleton& oth) = delete;
Singleton& operator=(const Singleton& oth) = delete;
Singleton(Singleton&& oth) = delete;
Singleton& operator=(Singleton&& oth) = delete;
static Singleton* get_instance() {
if (instance_ == nulllptr) {
instance_ = new Singleton;
}
return instance_;
}
private:
Singleton() = default;
static Singleton* instance_;
}
// 类加载时并不进行实例化
Singleton* Singleton::instance_ = nullptr;
int main() {
......
// 此时进行实例化
Singleton* ins = Singleton::get_instance();
......
}
复制代码
试想在多线程环境下,同时有两个线程调用了 get_instance(),它们看到的instance_ 都是nullptr,那么就会创建出两个实例!怎么解决呢?
互斥锁保护
class Singleton {
public:
Singleton(const Singleton& oth) = delete;
Singleton& operator=(const Singleton& oth) = delete;
Singleton(Singleton&& oth) = delete;
Singleton& operator=(Singleton&& oth) = delete;
static Singleton* get_instance() {
m_instance_.lock();
if (instance_ == nulllptr) {
instance_ = new Singleton;
}
m_instance_.unlock();
return instance_;
}
private:
Singleton() = default;
static Singleton* instance_;
static mutex m_instance_;
}
// 类加载时并不进行实例化
Singleton* Singleton::instance_ = nullptr;
mutex Singleton::m_instance_;
复制代码
这种简单利用互斥锁虽然可以解决线程安全问题,但是获取实例都必要上锁查抄(不仅第一次创建实例时必要上锁,纵然已经创建了实例仍然必要上锁查抄),效率实在低下!
双重查抄锁定
通过两个嵌套的if来判定单例对象是否为空的操作叫做双重查抄锁定
我们改进的思绪就是在加锁、解锁的代码块外层再添加一个判定,如许当任务队列的实例被创建出来之后,访问这个对象的线程就不会再实行加锁和解锁操作了。
class Singleton {
public:
Singleton(const Singleton& oth) = delete;
Singleton& operator=(const Singleton& oth) = delete;
Singleton(Singleton&& oth) = delete;
Singleton& operator=(Singleton&& oth) = delete;
static Singleton* get_instance() {
if (instance_ == nulllptr) {
m_instance_.lock();
if (instance_ == nullptr) {
instance_ = new Singleton;
}
m_instance_.unlock();
}
return instance_;
}
private:
Singleton() = default;
static Singleton* instance_;
static mutex m_instance_;
}
// 类加载时并不进行实例化
Singleton* Singleton::instance_ = nullptr;
mutex Singleton::m_instance_;
复制代码
万万没想到——可能会利用未创建好的实例?
到此仍然没有大功告成,因为万万没想到呆板指令会造成贫苦!
假设线程A实行到instance_ = new Singleton,线程B实行到第一个if (instance_ == nulllptr)。
instance_ = new Singleton在实行过程中对应的呆板指令可能会被重新排序。
正常过程如下:
分配内存
构造对象
指针指向分配的内存
但是被重新排序以后实行顺序可能会酿成如许:
分配内存
指针指向分配的内存。
构造对象
线程B在进行指针判定的时间instance_ 指针是不为空的,但这个指针指向的内存却没有被初始化!
怎么解决呢?利用atomic 原子操作(不可中断的操作)。
class Singleton {
public:
Singleton(const Singleton& oth) = delete;
Singleton& operator=(const Singleton& oth) = delete;
Singleton(Singleton&& oth) = delete;
Singleton& operator=(Singleton&& oth) = delete;
static Singleton* get_instance() {
Singleton* obj = instance_.load();
if (obj== nulllptr) {
m_instance_.lock();
obj = instance_.load();
if (obj == nullptr) {
obj = new Singleton();
instance_.store(obj);
}
m_instance_.unlock();
}
return obj;
}
private:
Singleton() = default;
static mutex m_instance_;
static atomic<Singleton*> instance_;
}
atomic<Singleton*> Singleton::instance_;
mutex Singleton::m_instance_;
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/)
Powered by Discuz! X3.4