线程安全的单例模式

单例模式:是非常典型常用的一种设计模式(大佬们针对典型场景设计的解决方案)

单例模式的特点
某些类,只应该具有一个对象(实例),就称之为单例。
例如一个男人只能有一个媳妇。
在很多服务器开发场景中,经常需要让服务器加载很多的数据(上百G)到内存中,此时往往要用一个单例的类来管理这些数据。

饿汉方式和懒汉方式

  1. 吃完饭立刻洗碗,这就是饿汉方式。因为下一顿吃的时候可以立刻拿着碗就能吃饭;
  2. 吃完饭,先把碗放下,然后下一顿饭用到这个碗了再洗碗,这就是懒汉方式

饿汉方式的实现
    使用static就可以—将一个成员变量设置为静态变量,则所有对象共用一份资源,并且在程序初始化时就会申请资源(不涉及线程安全)。

代码实现:

template <typename T>
class Singleton{
	static T data;
public:
	static T* GetInstance(){
		return &data;
	}
};

只要通过 Singleton这个包装类来使用T对象,则一个进程中只有一个T对象的实例。

懒汉方式的实现
    资源在使用的时候发现还没有加载,则申请加载。程序初始化比较快,第一次运行某个模块的时候就会比较慢,因为这时候去加载相应资源。

代码实现:

#include 
#include 
#include 

std::mutex g_mutex;

class single_instance{
public:
    volatile static int *get_instance(){
        // 不管—data资源是否已经被加载, 每次都要加锁判断, 然后解锁
        // 若资源已经加载过了, 加锁解锁就有点浪费资源, 并且容易造成锁冲突
        // 4. 二次判断, 避免每次都会加锁解锁
        if(_data == NULL){
            //-----------------//
            g_mutex.lock();// 3.实现线程安全
            if(_data == NULL){
                _data = new int;
                *_data = 10;
            }
            g_mutex.unlock();
        }
        return _data;
    }

private:
    //1.static 保证所有对象使用同一份资源
    //2.volatile 防止编译器过度优化
    volatile static int *_data;
};
volatile int *single_instance::_data = NULL;

实现所注意的细节

  1. 使用static保证所有对象使用同一份资源;
  2. 使用volatile,防止编译器过度优化
  3. 实现线程安全,保证资源判断以及申请过程是安全的
  4. 外部二次判断,以及避免资源已经加载成功每次获取都要加锁解锁,以及所带来的锁冲突。

你可能感兴趣的:(Linux,设计模式,多线程)