C++ concurrent multithreading--singleton design pattern and double check lock

Table of contents

1--Singleton design pattern

2--Double check lock

3--Use of std::call_once()


1--Singleton design pattern

        The singleton design pattern requires a certain class to create at most one object, which is the singleton object (a globally unique instance object);

        The singleton design pattern can be divided into: ① Lazy style (loading when the instance is needed); ② Hungry pattern (loading the instance from the beginning of the program);

        If a class object is complex and large, and can be reused, it will cause huge performance waste when frequently creating and destroying class objects. Designing this class object as a singleton object through the singleton design pattern can solve the problem the above questions;

        It is generally recommended to use nested classes to reclaim memory. The following code shows how to use nested classes to destroy objects and reclaim memory, and adopts a lazy design pattern;

#include <iostream>

class MyCAS{
public:
    static MyCAS *GetInstance(){
        if(m_instance == NULL){
            m_instance = new MyCAS(); 
            static CGar c1;
        }
        return m_instance;
    }

    void func(){
        std::cout << "test sample!" << std::endl;
    }

    class CGar{ // 类中套类,用于释放对象
    public:
        ~CGar(){
            if(MyCAS::GetInstance){
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };

private:
    MyCAS(){}; // 私有化成员变量,不能通过构造函数来创建对象
    static MyCAS *m_instance; // 静态成员变量

};

// 静态数据成员类外初始化
MyCAS* MyCAS::m_instance = NULL;


int main(int argc, char *argv[]){
    MyCAS *sample1 = MyCAS::GetInstance(); // 创建对象,返回该类对象的指针
    MyCAS *sample2 = MyCAS::GetInstance(); // 创建对象,返回该类对象的指针
    std::cout << sample1 << " " << sample2 << std::endl; // 两个指针指向同一个对象

    sample1->func(); // 调用成员函数测试
    return 0;
}

2--Double check lock

        Double-check locks can avoid data competition among multiple threads. Using double-check locks in the following code can prevent multiple threads from creating class objects at the same time (that is, execute new MyCAS(););

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1; // 互斥量

class MyCAS{
public:
    static MyCAS *GetInstance(){
        // 双重检查锁
        if(m_instance == NULL){
            std::unique_lock<std::mutex> guard1(mutex1); // 自动加锁和解锁
            if(m_instance == NULL){
                m_instance = new MyCAS();
                static CGar c1;
            }
        }
        return m_instance;
    }

    void func(){
        std::cout << "test sample!" << std::endl;
    }

    class CGar{ // 类中套类,用于释放对象
    public:
        ~CGar(){
            if(MyCAS::GetInstance){
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };

private:
    MyCAS(){}; // 私有化成员变量,不能通过构造函数来创建对象
    static MyCAS *m_instance; // 静态成员变量
};

// 静态数据成员类外初始化
MyCAS* MyCAS::m_instance = NULL;

// 线程入口函数
void mythread(){
    std::cout << "start thread" << std::endl;
    MyCAS *p_a = MyCAS::GetInstance();
    p_a->func();
    std::cout << "thread end" << std::endl;
    return;
}

int main(int argc, char *argv[]){
    std::thread thread1(mythread);
    std::thread thread2(mythread);
    thread1.join();
    thread2.join();
    return 0;
}

Problems with double-checked locks:

        When some members in a class object are more complex, the initialization time is relatively long; when a thread creates a singleton object, some of its members will not be initialized in the future; at this time, if other threads find that the singleton object has been created (existing address space ), you may choose to call the member function of the class object (but the complex member function is not initialized at this time), and an error will occur;

        At this time, you need to use the volatile keyword to ensure that the called member function is fully initialized;

3--Use of std::call_once()

        The function of std::call_once() is to ensure that the function func() will only be called once ; (that is, std::call_once() has the ability of mutex, which consumes less resources than mutex)

        std::call_once() needs to be used in conjunction with a flag that determines whether the object's function func() is called;

#include <iostream>
#include <thread>
#include <mutex>

std::once_flag g_flag; // 系统定义的标记

class MyCAS{
private:
    static void CreateInstance(){
        m_instance = new MyCAS();
        static CGar c1;
    }

public:
    static MyCAS *GetInstance(){
        // std::call_once 确保创建单例对象的函数只会被调用一次
        std::call_once(g_flag, CreateInstance);
        return m_instance;
    }

    void func(){
        std::cout << "test sample!" << std::endl;
    }

    class CGar{ // 类中套类,用于释放对象
    public:
        ~CGar(){
            if(MyCAS::GetInstance){
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };

private:
    MyCAS(){}; // 私有化成员变量,不能通过构造函数来创建对象
    static MyCAS *m_instance; // 静态成员变量
};

// 静态数据成员类外初始化
MyCAS* MyCAS::m_instance = NULL;

// 线程入口函数
void mythread(){
    std::cout << "start thread" << std::endl;
    MyCAS *p_a = MyCAS::GetInstance();
    p_a->func();
    std::cout << "thread end" << std::endl;
    return;
}

int main(int argc, char *argv[]){
    std::thread thread1(mythread);
    std::thread thread2(mythread);
    thread1.join();
    thread2.join();
    return 0;
}

Guess you like

Origin blog.csdn.net/weixin_43863869/article/details/132325740