我们的本意是多线程下执行每次使用getInstance()都返回同一个实例,即地址一致。
但从打印看到,地址不是同一个,说明多线程下instance被new了多次。
#include
#include
using namespace std;
class Singleton
{
private:
static Singleton* instance;
private:
Singleton() { cout << "instructor func..." << endl<
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJ1KGNB2-1661826670833)(C:\Users\tplink\AppData\Roaming\Typora\typora-user-images\image-20220829185232093.png)]
懒汉模式+互斥锁
可能两个线程判断instance为NULL,都进入line5.thread1加锁成功并new一个Singleton对象释放锁返回,但是thread2并不知道instance不为NULL,也获得锁new一个Singleton对象并返回。导致instance被new了两次。
static Singleton* getInstance()
{
if (instance == NULL)
{
mu.lock();
printf("instance is null!\n");
instance = new Singleton();
mu.unlock();
}
return instance;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D9oatdzD-1661826670835)(C:\Users\tplink\AppData\Roaming\Typora\typora-user-images\image-20220829190201143.png)]
懒汉模式+双检测锁模式
可以保证线程安全。
static Singleton* getInstance()
{
if (instance == NULL)
{
mu.lock();
if (instance == NULL)
{
printf("instance is null!\n");
instance = new Singleton();
}
mu.unlock();
}
return instance;
}
局部静态变量:
C++11规定了local static在多线程条件下的初始化行为,要求编译器保证了内部静态变量的线程安全性。在C++11标准下,《Effective C++》提出了一种更优雅的单例模式实现,使用函数内的 local static 对象。这样,只有当第一次访问getInstance()
方法时才创建实例。这种方法也被称为Meyers’ Singleton。C++0x之后该实现是线程安全的,C++0x之前仍需加锁。
// version 1.2
class Singleton
{
private:
Singleton() { };
~Singleton() { };
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
};
在程序中,如果new了一个对象,那么就应该负责对象的delete,但是如果这个对象是一个单例类的实例,那么对象销毁的时候就可能出现一些问题,例如Person是一个单例的类,但是同时有A,B,C三个人new了一个Person类的对象,那么A,B,C三个人得到的是同一个实例对象,但是可能发生A在用完这个对象之后执行了delete操作,导致仍然在使用这个对象的B,C出现错误。
使用静态嵌套类解决:main函数退出前调用deletor析构函数从而释放instance。
mutex mu;
class Singleton
{
private:
static Singleton* instance;
private:
Singleton() { cout << "instructor func..." << endl; };
~Singleton() { cout << "destructor func..." << endl; };
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
private:
class Deletor {
public:
~Deletor() {
if (Singleton::instance != NULL)
delete Singleton::instance;
}
};
static Deletor deletor;
public:
static Singleton* getInstance()
{
if (instance == NULL)
{
mu.lock();
if (instance == NULL)
{
printf("instance is null!\n");
instance = new Singleton();
}
mu.unlock();
}
return instance;
}
};
Singleton* Singleton::instance = NULL;
Singleton::Deletor Singleton::deletor;
int main()
{
thread t1(thread1);
thread t2(thread2);
t1.detach();
t2.detach();
for (int i = 0; i < 5; i++)
{
cout << "main thread working..." << endl;
Singleton* smain = Singleton::getInstance();
cout << "smain address is " << smain << endl;
}
return 0;
}
饿汉版(Eager Singleton):指单例实例在程序运行时被立即执行初始化。
由于在main函数之前初始化,所以没有线程安全的问题。但是潜在问题在于no-local static对象(函数外的static对象)在不同编译单元中的初始化顺序是未定义的。也即,static Singleton instance;和static Singleton& getInstance()二者的初始化顺序不确定,如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例。
// version 1.3
class Singleton
{
private:
static Singleton instance;
private:
Singleton();
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton& getInstance() {
return instance;
}
}
// initialize defaultly
Singleton Singleton::instance;