Multithreading simultané C++ - modèle de conception singleton et double contrôle de verrouillage

Table des matières

1-Modèle de conception singleton

2--Verrouillage à double contrôle

3--L'utilisation de std::call_once()


1-Modèle de conception singleton

        Le modèle de conception singleton nécessite une certaine classe pour créer au plus un objet, qui est l'objet singleton (un objet d'instance globalement unique) ;

        Le modèle de conception singleton peut être divisé en : ① Style paresseux (chargement lorsque l'instance est nécessaire) ② Modèle Hungry (chargement de l'instance depuis le début du programme) ;

        Si un objet de classe est complexe et volumineux et peut être réutilisé, cela entraînera un énorme gaspillage de performances lors de la création et de la destruction fréquentes d'objets de classe.La conception de cet objet de classe en tant qu'objet singleton via le modèle de conception singleton peut résoudre le problème des problèmes ci-dessus ;

        Il est généralement recommandé d'utiliser des classes imbriquées pour récupérer de la mémoire. Le code suivant montre comment utiliser des classes imbriquées pour détruire des objets et récupérer de la mémoire, et adopte un modèle de conception paresseux ;

#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--Verrouillage à double contrôle

        Les verrous de double vérification peuvent éviter la concurrence des données entre plusieurs threads. L'utilisation de verrous de double vérification dans le code suivant peut empêcher plusieurs threads de créer des objets de classe en même temps (c'est-à-dire exécuter 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;
}

Problèmes avec les serrures à double contrôle :

        Lorsque certains membres d'un objet de classe sont plus complexes, le temps d'initialisation est relativement long ; lorsqu'un thread crée un objet singleton, certains de ses membres ne seront pas initialisés à l'avenir ; à ce moment, si d'autres threads trouvent que l'objet singleton a été créé (espace d'adressage existant), vous pouvez choisir d'appeler la fonction membre de l'objet de classe (mais la fonction membre complexe n'est pas initialisée à ce moment), et une erreur se produira ;

        À ce stade, vous devez utiliser le mot clé volatile pour vous assurer que la fonction membre appelée est entièrement initialisée ;

3--L'utilisation de std::call_once()

        La fonction de std::call_once() est de s'assurer que la fonction func() ne sera appelée qu'une seule fois ; (c'est-à-dire que std::call_once() a la capacité de mutex, qui consomme moins de ressources que mutex)

        std::call_once() doit être utilisé en conjonction avec un indicateur qui détermine si la fonction func() de l'objet est appelée ;

#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;
}

Je suppose que tu aimes

Origine blog.csdn.net/weixin_43863869/article/details/132325740
conseillé
Classement