跳转至

Singleton单件模式

【单例类的定义】

class Singleton{
private:    //把构造函数设置成私有,让外界不能创建它
    Singleton();
    Singleton(const Singleton& other);
    static Singleton* m_instance;
public:
    static Singleton* getInstance();
};
Singleton* Singleton::m_instance=nullptr;

版本一:线程非安全

//[版本一] 线程非安全版本
Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {        //语句一
        m_instance = new Singleton();   //语句二
    }
    return m_instance;
}
/*
 假设对象还没有创建,m_instance现在为nullptr
 线程A、线程B两个一起进入语句二,一起执行语句二
 所以会导致,这个对象被创建多次,而真正被释放的只有一个
 */

版本二:线程安全

Singleton* Singleton::getInstance() {
    Lock lock; //加锁
    if (m_instance == nullptr) {        //读操作
        m_instance = new Singleton();   //写操作
    }
    return m_instance;  //读操作
} //退出以后,锁lock被释放,别的线程才能执行函数
/*
 只有写操作需要上锁,读操作是不需要上锁的,但在这个例子中,读操作也被上锁了。
 锁是有代价的,要等待别人完成,所以对读上锁,会造成浪费
 因此,在这个案例中,锁的代价过高。如果在高并发的场景下,这个代价是很高的
*/

版本三:线程安全,并双检查锁

//[版本三] 双检查锁(double check lock),但由于内存读写reorder不安全
Singleton* Singleton::getInstance() {
    if(m_instance==nullptr){ //是否空
        Lock lock;  //空才加锁
        if (m_instance == nullptr) {
           /*加完锁之后,还需要判断是否为空
            m_instance == nullptr判断是必要的!可以停下来想想为什么是必要的
            其实如果没有这句,执行情况和版本二是一样的,也会被创建两次
            */
           m_instance = new Singleton();
        }
    }
    return m_instance;
}

版本四:线程安全,跨平台

//C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
    if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);//释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

版本五:线程安全(std::call_once)

#include<mutex> //std::call_once

//多线程安全
glal::ModelImporter* ModelImporter::getInstance()
{
    static ModelImporter instance;

    //只执行一次初始化函数
    static std::once_flag oc;
    std::call_once(oc, &ModelImporter::_init, &instance);

    return &instance;
}

//初始化函数
void ModelImporter::_init()
{
    //初始化操作...
}
如果初始化函数做的事情比较少,也可以用匿名函数,比如
AlgoFactory* AlgoFactory::getInstance()
{
    static AlgoFactory instance;
    static std::once_flag oc;
    std::call_once(oc, []()
    {
        //加载插件
        int successSize = dan::SGPluginManager::instance()->loadPluginsByFolder("", dan::StringList() << "glal.algo.provider.*.dll");
    });
    return &instance;
}

参考文章

  1. 设计模式之单例模式(c++版) - SegmentFault 思否