再谈单例模式
2025-06-28 10:03:11
  • 全局范围内,某个类的实例有且仅有一个,通过这个唯一实例向其他模块提供数据的全局访问,这种模式即为单例模式(典型应用:任务队列

饿汉模式

  • 饿汉模式即在类加载时立刻进行实例化,是线程安全的!
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
class TaskQueue
{
public:
// = delete 代表函数禁用, 也可以将其访问权限设置为私有
TaskQueue(const TaskQueue& obj) = delete;
TaskQueue& operator=(const TaskQueue& obj) = delete;

// 提供给外部的接口,用于访问唯一实例
static TaskQueue* getInstance()
{
return m_taskQ;
}

// 提供销毁实例的方法
static void releaseInstance()
{
delete m_taskQ;
m_taskQ = nullptr;
}
private:
TaskQueue() = default;
static TaskQueue* m_taskQ;
};

// 静态成员初始化放到类外部处理
TaskQueue* TaskQueue::m_taskQ = new TaskQueue;

int main()
{
TaskQueue* obj = TaskQueue::getInstance();

TaskQueue::releaseInstance();

return 0;
}

懒汉模式

  • 懒汉模式是在类加载的时候不去创建这个唯一的实例,而是在需要使用的时候再进行实例化,并非线程安全的。
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
class TaskQueue
{
public:
// = delete 代表函数禁用, 也可以将其访问权限设置为私有
TaskQueue(const TaskQueue& obj) = delete;
TaskQueue& operator=(const TaskQueue& obj) = delete;
// 提供给外部的接口,用于访问唯一实例
static TaskQueue* getInstance()
{
if(m_taskQ == nullptr)
{
m_taskQ = new TaskQueue;
}
return m_taskQ;
}

// 提供销毁实例的方法
static void releaseInstance()
{
delete m_taskQ;
m_taskQ = nullptr;
}
private:
TaskQueue() = default;
static TaskQueue* m_taskQ;
};

// 静态成员初始化放到类外部处理
TaskQueue* TaskQueue::m_taskQ = nullptr;

注:单线程情况下调用getInstance()函数获取单例对象是没有问题的,但若是多线程情况,多个线程同时执行getInstance()函数,这时每个线程都会new出一个实例对象,与单例模式的定义相悖,故懒汉模式存在线程安全问题!

二者的区别

  • 懒汉模式的缺点是在创建实例对象的时候有安全问题,但这样可以减少内存的浪费(如果用不到就不去申请内存了)。饿汉模式则相反,在我们不需要这个实例对象的时候,它已经被创建出来,占用了一块内存。对于现在的计算机而言,内存容量都是足够大的,这个缺陷可以被无视。

利用静态局部变量(C++11)的懒汉模式

  • C++11保证静态局部变量的初始化是线程安全!
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
#include <iostream>

class Singleton {
private:
Singleton() {} // 私有构造函数

public:
// 禁止拷贝和赋值操作
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

static Singleton& getInstance() {
static Singleton instance; // 静态局部变量,线程安全 (C++11 保证)
return instance;
}

void doSomething() {
std::cout << "Static Local Variable Singleton instance" << std::endl;
}
};

int main() {
Singleton& singleton = Singleton::getInstance();
singleton.doSomething();
return 0;
}

结合智能指针的懒汉模式

使用智能指针来管理静态局部对象,真正的Singleton对象是动态分配在堆上,unique_ptr负责管理它的生命周期。
而使用静态局部对象Singleton对象存放在静态存储区。
使用上两者差不多,但智能指针版本可以自定义删除其、统计分配信息等。

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
#include <iostream>
#include <memory>

class Singleton {
private:
// 私有构造函数,防止外部实例化
Singleton() = default;

public:
// 禁止拷贝和赋值操作
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

static Singleton& getInstance() {
static std::unique_ptr<Singleton> instance(new Singleton());
return *instance;
}

void doSomething() {
std::cout << "Static Local Variable Singleton instance" << std::endl;
}
};

int main() {
Singleton& singleton = Singleton::getInstance();
singleton.doSomething();

return 0;
}

基于饿汉模式的任务队列

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// 基于饿汉模式的线程安全的任务队列
#include <iostream>
#include <queue>
#include <mutex>
#include <thread>

class TaskQueue
{
public:
// 提供给外部的接口,用于访问唯一实例
static TaskQueue* getInstance()
{
return &m_obj;
}

// = delete 代表函数禁用, 也可以将其访问权限设置为私有
TaskQueue(const TaskQueue& obj) = delete;
TaskQueue& operator=(const TaskQueue& obj) = delete;

// 任务队列是否为空
bool isEmpty()
{
std::lock_guard<std::mutex> locker(m_mutex);
bool flag = m_taskQ.empty();
return flag;
}

// 添加任务
void addTask(int data)
{
std::lock_guard<std::mutex> locker(m_mutex);
m_taskQ.push(data);
}

// 取出任务
int takeTask()
{
std::lock_guard<std::mutex> locker(m_mutex);
if (!m_taskQ.empty())
{
return m_taskQ.front();
}
return -1;
}

// 删除任务
bool popTask()
{
std::lock_guard<std::mutex> locker(m_mutex);
if (!m_taskQ.empty())
{
m_taskQ.pop();
return true;
}
return false;
}

private:
TaskQueue() = default;
static TaskQueue m_obj;
std::queue<int> m_taskQ;
std::mutex m_mutex; // 互斥锁,用以确保queue操作的线程安全
};

// 静态成员初始化放到类外部处理
TaskQueue TaskQueue::m_obj;

int main()
{
std::thread t1([](){
TaskQueue* taskQ = TaskQueue::getInstance();
for (int i = 0; i < 100; ++i)
{
taskQ->addTask(i + 100);
std::cout << "+++push task: " << i + 100 << ", threadID: " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
});

std::thread t2([](){
TaskQueue* taskQ = TaskQueue::getInstance();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
while (!taskQ->isEmpty())
{
int data = taskQ->takeTask();
std::cout << "---task task: " << data << ", threadID: " << std::this_thread::get_id() << std::endl;
taskQ->popTask();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});

t1.join();
t2.join();

return 0;
}
Prev
2025-06-28 10:03:11
Next