单例模式

scorlw 发布于

单例模式

设计模式

单例模式

保证一个类只有一个实例,并提供一个访问它的全局访问点。

image-20200926115344338

根据对象初始化的位置不同,单例模式有懒汉式和饿汉式两种。

懒汉式

调用getInstance时才构造对象。

优点:用不到就不新建对象,节省了空间;

缺点:每次getInstance时都需要进行判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//懒汉式:只有调用时才初始化对象
class Singleton_l
{
private:
Singleton_l()
{
cout << "构造函数运行" << endl;
};
~Singleton_l();

public:
static Singleton_l* getInstance()
{
if(m_instance == NULL)
{
m_instance = new Singleton_l;
}
return m_instance;
}

static void freeInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
}
}

private:
static Singleton_l* m_instance;
};

Singleton_l* Singleton_l::m_instance = NULL;

饿汉式

程序开始就新建了对象。

优点:不需要每次都判断;

缺点:提前占用了空间。且由于需要提前申请,有些时候不能使用饿汉式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//饿汉式:初始化时就需要new出实例
class Singleton_e
{
private:
Singleton_e()
{
cout << "构造函数运行" << endl;
};
~Singleton_e();

public:
static Singleton_e* getInstance()
{
return m_instance;
}

static void freeInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
}
}

private:
static Singleton_e* m_instance;
};

//记得初始化
Singleton_e* Singleton_e::m_instance = new Singleton_e();

总:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <iostream>

using namespace std;

//懒汉式:只有调用时才初始化对象
class Singleton_l
{
private:
Singleton_l()
{
cout << "构造函数运行" << endl;
};
~Singleton_l();

public:
static Singleton_l* getInstance()
{
if(m_instance == NULL)
{
m_instance = new Singleton_l;
}
return m_instance;
}

static void freeInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
}
}

private:
static Singleton_l* m_instance;
};

//饿汉式:初始化时就需要new出实例
class Singleton_e
{
private:
Singleton_e()
{
cout << "构造函数运行" << endl;
};
~Singleton_e();

public:
static Singleton_e* getInstance()
{
return m_instance;
}

static void freeInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
}
}

private:
static Singleton_e* m_instance;
};

//记得初始化
Singleton_l* Singleton_l::m_instance = NULL;
Singleton_e* Singleton_e::m_instance = new Singleton_e();
int main()
{
Singleton_l* p1 = Singleton_l::getInstance();
Singleton_l* p2 = Singleton_l::getInstance();
if(p1 == p2){
cout << "一个实例" << endl;
}
cout << "Hello world!" << endl;
return 0;
}

多线程时的单例模式

多线程时单例模式的问题

出现在懒汉式单例模式中。如果多个线程都调用getInstance函数,可能出现多个线程都构造了新的对象,这就不是单例模式了。也就是说,构造函数不是线程安全函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <windows.h>

using namespace std;

//懒汉式:只有调用时才初始化对象
class Singleton_l
{
private:
Singleton_l()
{
cout << "构造函数运行begin" << endl;
Sleep(1000);//睡眠,释放CPU资源
cout << "构造函数运行end" << endl;
};
~Singleton_l();

public:
static Singleton_l* getInstance()
{
if(m_instance == NULL)
{
cnt++;
m_instance = new Singleton_l;
}
return m_instance;
}

static void freeInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
cnt--;
}
}

void prints()
{
cout << "instance print test" << endl;
}

int getCnt()
{
return cnt;
}

private:
static Singleton_l* m_instance;
static int cnt;
};
//记得初始化
Singleton_l* Singleton_l::m_instance = NULL;
int Singleton_l::cnt = 0;

void MyThreadFunc (void*)
{
cout << "newthread" << endl;//可能会出现连续两个newthreadnewthread的情况,是因为endl时输出内存区里的所有东西。如果想避免这种情况,则将endl改为\n
Singleton_l::getInstance()->prints();
}
int main()
{
//句柄
HANDLE hThread[10];
//建立三个新线程
for (int i = 0; i < 3; i++)
hThread[i] = (HANDLE)_beginthread(MyThreadFunc, 0, NULL);
//等待子线程的结束,防止主进程在子线程结束前结束
for (int i = 0; i < 3; i++)
WaitForSingleObject(hThread[i],INFINITE);
cout << "Hello world!" << endl;
cout << Singleton_l::getInstance()->getCnt() << endl;//3
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输出:
newthreadnewthread
构造函数运行begin

构造函数运行begin
newthread
构造函数运行begin
构造函数运行end
构造函数运行end
instance print test
构造函数运行endinstance print test

instance print test
Hello world!
3

解决方法

  1. 双重锁定:进行两次检查,不用让线程每次都加锁
  2. 加锁操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <windows.h>
#include <mutex>

using namespace std;

mutex mut;//锁

//懒汉式:只有调用时才初始化对象
class Singleton_l
{
private:
Singleton_l()
{
cout << "构造函数运行begin" << endl;
Sleep(1000);
cout << "构造函数运行end" << endl;
};
~Singleton_l();

public:
static Singleton_l* getInstance()
{
if(m_instance == NULL)//第一次检查,判断是否需要新建实例
{
mut.lock();
if(m_instance == NULL)//第二次检查,判断是否已经有线程新建了实例
{
cnt++;
m_instance = new Singleton_l;
}
mut.unlock ();
}
return m_instance;
}

static void freeInstance()
{
if(m_instance != NULL)
{
delete m_instance;
m_instance = NULL;
cnt--;
}
}

void prints()
{
cout << "instance print test" << endl;
}

int getCnt()
{
return cnt;
}

private:
static Singleton_l* m_instance;
static int cnt;
};

//记得初始化
Singleton_l* Singleton_l::m_instance = NULL;
int Singleton_l::cnt = 0;

void MyThreadFunc (void*)
{
cout << "newthread" << endl;
Singleton_l::getInstance()->prints();
}
int main()
{
HANDLE hThread[10];
for (int i = 0; i < 3; i++)
hThread[i] = (HANDLE)_beginthread(MyThreadFunc, 0, NULL);

for (int i = 0; i < 3; i++)
WaitForSingleObject(hThread[i],INFINITE);
cout << "Hello world!" << endl;
cout << Singleton_l::getInstance()->getCnt() << endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
输出:
newthreadnewthread

newthread
构造函数运行begin
构造函数运行end
instance print test
instance print test
instance print test
Hello world!
1