12. Singleton mode of object performance mode (Sigleton)

 object performance model

  • Object-oriented solves the "abstract" problem very well, but it is inevitable to pay a certain price. Generally speaking, the cost of object-oriented is mostly negligible, but in some cases, the cost of object-oriented must be handled carefully
  • typical pattern
    • Sigleton
    • Flyweight

----------------------------------------------------------------------------------------------

singleton pattern

1. Motivation

  • In software systems, there are often such special classes that must be guaranteed to have only one instance in the system to ensure their logical correctness and good efficiency.
  • How to bypass conventional constructors and provide a mechanism to ensure that a class has only one instance?
  • This should be the responsibility of the designer of the class, not the user

2. Code

class Sigleton{
private:
    Sigleton();
    Sigleton(const Sigleton& other);

public:
    static Sigleton*getInstance();
    
    static Sigleton* m_instance;
};

Sigleton* Sigleton::m_instance = nullptr;

// 线程非安全版本
Sigleton* Sigleton::getInstance(){
    if (m_instance == nullptr){
        m_instance = new Sigleton();
    }
    
    return m_instance;
}


// 线程安全,但锁的代价过高
Sigleton* Sigleton::getInstance(){
    Lock lock;
    if (m_instance == nullptr){
        m_instance = new Sigleton();
    }

    return m_instance;
}

// 双检查锁,但由于内存读写reorder不安全
Sigleton* Sigleton::getInstance(){
    if (m_instance == nullptr){
        Lock lock;
        if (m_instance == nullptr){       
            // 下面New的大概步骤
            // Step1. 先分配内存
            // Step2. 调用Sigleton构造器,对刚才分配的内存进行初始化
            // Step3. 将刚才分配的地址赋值给m_instance
            // 但是在CPU指令级别,上面的步骤有可能会被reorder
            // 编译器优化后,有可能会变成:
            // Step1 -> Step3 -> Step2
            // 如果此时执行到Step3,切换到另外一个线程,最外面的判断将是非空,然后直接去使用这个还没有构造的m_instance,导致错误
            m_instance = new Sigleton(); // 2000年左右才被一个java领域的专家发现这个BUG
        }
    }
    return m_instance;
}

// C++11版本之后的跨平台版本

std::atomic<Sigleton*> Sigleton::m_instance = nullptr;
std::mutex Sigleton::m_mutex;

Sigleton* Sigleton::getInstance(){

    Sigleton* 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 Sigleton(); 
            std::atomic_thread_fence(std::memory_order_relaxed); // 释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

3. Schema definition

Ensures that there is only one instance of a class and provides a global access point to that instance.

                                                                                ---《Design Patterns》GOF

4. Structure

 5. Summary

  • Instance constructors in the Sigleton pattern can be set to protected to allow subclassing.
  • Sigleton mode generally does not support copy constructor and Clone interface, because this may result in multiple object instances

Guess you like

Origin blog.csdn.net/bocai1215/article/details/127561538