目录
什么是单例模式?
单例模式属于简单设计模式的一种。在整个系统的生命周期内,单例类有且只有唯一一个对象,典型的应用比如日志的句柄。使用单例模式时需要考虑线程安全的问题,具体看后文具体的代码解析。
单例模式的特点
- 单例类只能有一个实例。
- 成员是私有的、静态的。
- 禁止拷贝、赋值,构造函数、私有函数是私有的。
单例模式的实现方式
- 懒汉模式:在需要使用的时候才实例化对象。
- 饿汉模式:在系统刚启动时就实例化对象。
懒汉模式
实现一(非线程安全)
#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
对象,因此,后续访问的都是同一个对象,是天然的线程安全的。
总结
单例模式是一种比较经典、常用的设计模式,面试也经常会问到,是一定要掌握的。如果在程序中需要创建一个唯一存在的实例对象,那么一定要考虑使用单例模式,优先使用懒汉模式中的返回局部静态变量的方法,切记保证线程安全。
标签:std,Singleton,模式,threads,单例,线程,include From: https://www.cnblogs.com/Joe-zhu/p/18329136