直接看代码:
/*
单例模式可以保证:在一个程序当中,一个类有且只有一个实例,并提供一个访问
它的全局访问点
在程序设计当中,很多情况下需要确保一个类只有一个实例
例如: windopws系统中只能有一个窗口管理器
某个程序中只能有一个日志输出系统
一个GUI系统类库中,有且只有一个ImageManager
*/
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
//Meyers Singleton Pattern实现
class CSingleton2
{
public:
//单例对象使用局部静态变量方式从而使之延迟到调用的时候实例化
static CSingleton2& GetInstance()
{
static CSingleton2 sg;
return sg;
}
void Print()
{
printf("print Singleton2 count = %d\n",m_count);
}
private:
int m_count;
//构造函数私有化,让外部不能访问,达到只能有一个实例对象的效果
CSingleton2()
{
printf("开始 construct Singleton2 count = %d\n",m_count);
::Sleep(1000);//这里是为了看到效果
m_count = 100;
printf("结束 construct Singleton2 count = %d\n",m_count);
}
public:
~CSingleton2()
{
printf("调用析构函数\n");
}
private:
//防止拷贝构造和赋值操作
CSingleton2(const CSingleton2&);
CSingleton2& operator=(const CSingleton2&);
};
unsigned int __stdcall thread(void*)
{
printf("current Thread ID = %d\n", ::GetCurrentThreadId());
CSingleton2::GetInstance().Print();
return 0;
}
void TestMultThread()
{
//这里创建三个线程
for(int i = 0; i < 3; i++)
{
HANDLE t = (HANDLE)::_beginthreadex(NULL,0,thread,NULL,0,NULL);
::CloseHandle((HANDLE)t);
}
}
int main()
{
TestMultThread();
getchar();
return 0;
}
/*
1:优点:
1):该实现是一个“懒汉”单例模式,意味着只有在第一次调用GetInstance()的时候才会实例化
2):不需要每次调用GetInstance()静态方法时,必须判断NULL==m_instance,效率相对高一点
3):使用对象而不是指针分配内存,因此自动调用析构函数,不会导致内存泄露
4):在多线程下的确能够保证有且只有一个实例产生。
2:缺点:
在多线程情况下,并不是真正的线程安全
*/
/*
current Thread ID = 1148
开始 construct Singleton2 count = 0 --- 假设1148线程创建单件实例,分配内存,但是还未初始化实例的成员变量
current Thread ID = 6668
print Singleton2 count = 0
current Thread ID = 6892
print Singleton2 count = 0 -- 线程6668拿到所有权,此时Singleton2内存已经分配好了,但是成员变量还没初始化,所以调用Print,m_count = 0;
结束 construct Singleton2 count = 100 -- 6892拿到线程所有权,继续进行成员变量的初始化,然后调用Print函数,输出100;
print Singleton2 count = 100
调用析构函数 -- 一次析构函数,说明生成了一个实例对象
Press any key to continue
原因:
这是因为C++构造函数并不是线程安全的。
C++中的构造函数简单来说分两步:
第一步:内存分配
第二步:初始化成员变量
由于多线程的关系,可能当我们在分配内存好了以后,还没来得急初始化成员变量,就
进行线程切换,另外一个线程拿到所有权后,由于内存已经分配好了,但是变量初始化
还没进行,因此打印成员变量的相关值会发生不一致现象。
结论:Meyers 方式虽然能确保在多线程中产生唯一的实例,但是不能确保成员变量的值是否正确。
*/