前言
单例模式有懒汉式和饿汉式。
两者的主要区别在于:
- 懒汉式:第一次调用时,才会进行初始化;
- 饿汉式:程序加载时,就会进行初始化;
懒汉式和饿汉式还有一个不同点:
饿汉式是线程安全的,懒汉式不是。在多线程环境下,懒汉式的线程安全问题是一个重要问题。
内容
常见的处理懒汉式的线程安全问题一般是 加锁 和 双重检查
#include "iostream"
#include "shared_mutex"
#include "mutex"
using namespace std;
class Singleton
{
public:
static Singleton *Instance()
{
if (ptr == nullptr)
{
std::shared_lock<shared_mutex> lock(m_mutex);
if(ptr == nullptr)
{
ptr = new Singleton;
}
}
return ptr;
}
private:
static Singleton *ptr;
static shared_mutex m_mutex;
private:
Singleton() = default;
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
};
Singleton *Singleton::ptr = nullptr;
我们也可以使用标准库中的call_once来进行单例懒汉式初始化
#include "iostream"
#include "shared_mutex"
#include "mutex"
#include "thread"
using namespace std;
class Singleton
{
public:
static Singleton *Instance()
{
if(ptr == nullptr)
{
std::call_once(flag, [&]()
{
ptr = new Singleton;
});
}
else
{
cout<<"hello mama"<<endl;
}
return ptr;
}
private:
static Singleton *ptr;
static std::once_flag flag;
private:
Singleton(){
cout<<"hello word"<<endl;
}
~Singleton(){
delete ptr;
ptr = nullptr;
}
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
};
Singleton *Singleton::ptr = nullptr;
std::once_flag Singleton::flag;
void test(void)
{
Singleton::Instance();
}
int main(void)
{
std::thread t1(test);
std::thread t2(test);
std::thread t3(test);
t1.join();
t2.join();
t3.join();
cout << "end" << endl;
return 0;
}
单例模式中使用call_once(),让模块在第一次被线程调用时就进行初始化
使用std::call_once函数时,首先需要创建一个std::once_flag对象,用于标记函数是否已经被调用。然后,在多个线程中调用std::call_once函数,并传入相同的std::once_flag对象和要执行的函数。在第一个调用std::call_once的线程中,函数会被执行一次,而在其它调用线程中,函数会被跳过。