单例模式
在全局范围中,某个类的对象只有一个,通过这个唯一的实例向其他模块提供数据的全局访问。
需要:
- 默认构造函数私有化
- 因为使用者在类外部无法创建类对象,故在类内创建静态对象通过类名来访问
- 在类中只有静态成员函数才能访问静态成员变量,故提供一个静态成员函数提供该静态对象
- 拷贝构造函数私有化或禁用
- 拷贝复制运算符私有化或禁用
饿汉模式
在类加载时立即实例化,无线程安全问题
class TaskQueue {
public:
TaskQueue(const TaskQueue &t) = delete;
TaskQueue &operator=(const TaskQueue &t) = delete;
static TaskQueue *get_instance() { return m_taskQ; }
private:
TaskQueue() = default;
static TaskQueue *m_taskQ;
};
TaskQueue *TaskQueue::m_taskQ = new TaskQueue;
int main()
{
TaskQueue *t = TaskQueue::get_instance();
}
懒汉模式
在加载时不创建唯一实例,直到使用才创建,有线程安全问题
class TaskQueue {
public:
TaskQueue(const TaskQueue &t) = delete;
TaskQueue &operator=(const TaskQueue &t) = delete;
static TaskQueue *get_instance() {
if (m_taskQ == nullptr) {
m_taskQ = new TaskQueue;
}
return m_taskQ;
}
private:
TaskQueue() = default;
static TaskQueue *m_taskQ;
};
TaskQueue *TaskQueue::m_taskQ = nullptr;
线程安全问题处理
双重检查锁定
class TaskQueue {
public:
TaskQueue(const TaskQueue &t) = delete;
TaskQueue &operator=(const TaskQueue &t) = delete;
static TaskQueue *get_instance() {
if (m_taskQ == nullptr) {
m_mutex.lock();
if (m_taskQ == nullptr) {
m_taskQ = new TaskQueue;
}
m_mutex.unlock();
}
return m_taskQ;
}
private:
TaskQueue() = default;
static TaskQueue *m_taskQ;
static mutex m_mutex;
};
TaskQueue *TaskQueue::m_taskQ = nullptr;
mutex TaskQueue::m_mutex;
上述代码在 m_taskQ = new TaskQueue 处由于机器指令重排加上恰好时间片结束,仍然会出现线程安全问题
class TaskQueue {
public:
TaskQueue(const TaskQueue &t) = delete;
TaskQueue &operator=(const TaskQueue &t) = delete;
static TaskQueue *get_instance() {
TaskQueue *queue = m_taskQ.load();
if (queue == nullptr) {
lock_gard<mutex> locker(m_mutex);
queue = m_taskQ.load();
if (queue == nullptr) {
queue = new TaskQueue;
m_taskQ.store(queue);
}
}
return queue;
}
private:
TaskQueue() = default;
static atomic<TaskQueue *> m_taskQ;
static mutex m_mutex;
};
atomic<TaskQueue *> TaskQueue::m_taskQ;
mutex TaskQueue::m_mutex;
静态局部对象
class TaskQueue {
public:
TaskQueue(const TaskQueue &t) = delete;
TaskQueue &operator=(const TaskQueue &t) = delete;
static TaskQueue *get_instance() {
static TaskQueue taskQ;
return &taskQ;
}
private:
TaskQueue() = default;
};