- 全局范围内,某个类的实例有且仅有一个,通过这个唯一实例向其他模块提供数据的全局访问,这种模式即为单例模式(典型应用:任务队列)
饿汉模式
- 饿汉模式即在类加载时立刻进行实例化,是线程安全的!
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: 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: 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)的懒汉模式
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; 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; }
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; };
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; }
|