目录
什么是单例模式?
单例模式属于简单设计模式的一种。在整个系统的生命周期内,单例类有且只有唯一一个对象,典型的应用比如日志的句柄。使用单例模式时需要考虑线程安全的问题,具体看后文具体的代码解析。
单例模式的特点
- 单例类只能有一个实例。
- 成员是私有的、静态的。
- 禁止拷贝、赋值,构造函数、私有函数是私有的。
单例模式的实现方式
- 懒汉模式:在需要使用的时候才实例化对象。
- 饿汉模式:在系统刚启动时就实例化对象。
懒汉模式
实现一(非线程安全)
#include <iostream>
#include <array>
#include <thread>
#include <mutex>
#define MAX_THREAD_SIZE 10
class Singleton
{
public:
static Singleton* GetInstance();
static void DeleteInstance();
void Print(int index);
Singleton(const Singleton& kSingleton) = delete;
Singleton* operator=(const Singleton& kSingleton) = delete;
private:
Singleton();
~Singleton();
static Singleton* singleton_;
};
Singleton::Singleton()
{
std::cout << "构造函数" << std::endl;
}
Singleton::~Singleton()
{
std::cout << "析构函数" << std::endl;
}
Singleton* Singleton::GetInstance()
{
if (!singleton_)
{
singleton_ = new(std::nothrow) Singleton;
}
return singleton_;
}
void Singleton::DeleteInstance()
{
if (singleton_)
{
delete singleton_;
singleton_ = nullptr;
}
}
void Singleton::Print(int index)
{
std::cout << "线程" << index << ":" << this << std::endl;
}
Singleton* Singleton::singleton_ = nullptr;
int main()
{
std::array<std::thread, MAX_THREAD_SIZE> threads;
for (int i = 0; i < MAX_THREAD_SIZE; i++)
{
threads[i] = std::thread(&Singleton::Print, Singleton::GetInstance(), i);
threads[i].join();
}
Singleton::DeleteInstance();
return 0;
}
此种实现方式,可能会有两个线程同时进入GetInstance()
函数,恰好同时判断出singleton_指针为空,各自new了一个Singleton对象,所以是非线程安全的,如果想要此种实现是线程安全的,那么对GetInstance()
实现加上锁保护即可,详见实现二。
实现二(线程安全)
#include <iostream>
#include <array>
#include <thread>
#include <mutex>
#define MAX_THREAD_SIZE 10
class Singleton
{
public:
static Singleton* GetInstance();
static void DeleteInstance();
void Print(int index);
Singleton(const Singleton& kSingleton) = delete;
Singleton* operator=(const Singleton& kSingleton) = delete;
private:
Singleton();
~Singleton();
static Singleton* singleton_;
static std::mutex mutex_;
};
Singleton::Singleton()
{
std::cout << "构造函数" << std::endl;
}
Singleton::~Singleton()
{
std::cout << "析构函数" << std::endl;
}
Singleton* Singleton::GetInstance()
{
if (!singleton_)
{
std::unique_lock<std::mutex> lock(mutex_);
if (!singleton_)
{
singleton_ = new(std::nothrow) Singleton;
}
}
return singleton_;
}
void Singleton::DeleteInstance()
{
std::unique_lock<std::mutex> lock(mutex_);
if (singleton_)
{
delete singleton_;
singleton_ = nullptr;
}
}
void Singleton::Print(int index)
{
std::cout << "线程" << index << ":" << this << std::endl;
}
Singleton* Singleton::singleton_ = nullptr;
std::mutex Singleton::mutex_;
int main()
{
std::array<std::thread, MAX_THREAD_SIZE> threads;
for (int i = 0; i < MAX_THREAD_SIZE; i++)
{
threads[i] = std::thread(&Singleton::Print, Singleton::GetInstance(), i);
threads[i].join();
}
Singleton::DeleteInstance();
return 0;
}
通过在GetInstance()
函数中添加锁的保护,可以保证有且只有一个线程进入并创建了Singleton
类对象,从而保证了线程安全,但是多了锁的开销,那么有没有更好的方法呢?下面介绍C++11后最推荐的方式。
实现三(线程安全、推荐)
#include <iostream>
#include <array>
#include <thread>
#include <mutex>
#define MAX_THREAD_SIZE 10
class Singleton
{
public:
static Singleton& GetInstance();
void Print(int index);
Singleton(const Singleton& kSingleton) = delete;
Singleton& operator=(const Singleton& kSingleton)= delete;
private:
Singleton();
~Singleton();
};
Singleton::Singleton()
{
std::cout << "构造函数" << std::endl;
}
Singleton::~Singleton()
{
std::cout << "析构函数" << std::endl;
}
Singleton& Singleton::GetInstance()
{
static Singleton singleton_;
return singleton_;
}
void Singleton::Print(int index)
{
std::cout << "线程" << index << ":" << this << std::endl;
}
int main()
{
std::array<std::thread, MAX_THREAD_SIZE> threads;
for (int i = 0; i < MAX_THREAD_SIZE; i++)
{
threads[i] = std::thread(&Singleton::Print, &Singleton::GetInstance(), i);
threads[i].join();
}
return 0;
}
此种实现适用于C++11之后的程序,因为C++11规定:如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。 这种返回局部静态变量的方式,更加的简洁高效,所以比较推荐。
饿汉模式
#include <iostream>
#include <array>
#include <thread>
#include <mutex>
#define MAX_THREAD_SIZE 10
class Singleton
{
public:
static Singleton* GetInstance();
static void DeleteInstance();
void Print(int index);
Singleton(const Singleton& kSingleton) = delete;
Singleton& operator=(const Singleton& kSingleton) = delete;
private:
Singleton();
~Singleton();
static Singleton* singleton_;
};
Singleton::Singleton()
{
std::cout << "构造函数" << std::endl;
}
Singleton::~Singleton()
{
std::cout << "析构函数" << std::endl;
}
Singleton* Singleton::GetInstance()
{
return singleton_;
}
void Singleton::DeleteInstance()
{
if (singleton_)
{
delete singleton_;
singleton_ = nullptr;
}
}
void Singleton::Print(int index)
{
std::cout << "线程" << index << ":" << this << std::endl;
}
Singleton* Singleton::singleton_ = new(std::nothrow) Singleton;
int main()
{
std::array<std::thread, MAX_THREAD_SIZE> threads;
for (int i = 0; i < MAX_THREAD_SIZE; i++)
{
threads[i] = std::thread(&Singleton::Print, Singleton::GetInstance(), i);
threads[i].join();
}
Singleton::DeleteInstance();
return 0;
}
饿汉模式的实现,在程序启动时,就已经实例化了Singleton
对象,因此,后续访问的都是同一个对象,是天然的线程安全的。
总结
单例模式是一种比较经典、常用的设计模式,面试也经常会问到,是一定要掌握的。如果在程序中需要创建一个唯一存在的实例对象,那么一定要考虑使用单例模式,优先使用懒汉模式中的返回局部静态变量的方法,切记保证线程安全。
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容